/*****************************************************************
BioZen
Copyright (C) 2011 The National Center for Telehealth and
Technology
Eclipse Public License 1.0 (EPL-1.0)
This library is free software; you can redistribute it and/or
modify it under the terms of the Eclipse Public License as
published by the Free Software Foundation, version 1.0 of the
License.
The Eclipse Public License is a reciprocal license, under
Section 3. REQUIREMENTS iv) states that source code for the
Program is available from such Contributor, and informs licensees
how to obtain it in a reasonable manner on or through a medium
customarily used for software exchange.
Post your updates and modifications to our GitHub or email to
t2@tee2.org.
This library is distributed WITHOUT ANY WARRANTY; without
the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the Eclipse Public License 1.0 (EPL-1.0)
for more details.
You should have received a copy of the Eclipse Public License
along with this library; if not,
visit http://www.opensource.org/licenses/EPL-1.0
*****************************************************************/
package com.t2.compassionMeditation;
import java.io.File;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;
import org.achartengine.model.XYSeries;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.t2health.lib1.BioParameter;
import org.t2health.lib1.BioSensor;
import org.t2health.lib1.dsp.T2MovingAverageFilter;
import bz.org.t2health.lib.activity.BaseActivity;
import bz.org.t2health.lib.analytics.Analytics;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.stmt.QueryBuilder;
import com.oregondsp.signalProcessing.filter.iir.ChebyshevI;
import com.t2.SpineReceiver;
import com.t2.SpineReceiver.BioFeedbackStatus;
import com.t2.SpineReceiver.OnBioFeedbackMessageRecievedListener;
import com.t2.antlib.ANTPlusService;
import com.t2.antlib.AntPlusManager;
import com.t2.biofeedback.activity.BTServiceManager;
import com.t2.biofeedback.device.shimmer.ShimmerDevice;
import com.t2.compassionDB.BioSession;
import com.t2.compassionDB.BioUser;
import com.t2.compassionUtils.MathExtra;
import com.t2.compassionUtils.TMovingAverageFilter;
import com.t2.compassionUtils.RateOfChange;
import com.t2.compassionUtils.Util;
import com.t2.dataouthandler.DataOutHandler;
import com.t2.dataouthandler.DataOutHandlerException;
import com.t2.dataouthandler.DataOutHandlerTags;
import com.t2.dataouthandler.DataOutPacket;
import com.t2.t2sensorlib.BigBrotherService;
import com.t2.Constants;
import spine.datamodel.Node;
import spine.SPINEFactory;
import spine.SPINEFunctionConstants;
import spine.SPINEListener;
import spine.SPINEManager;
import spine.SPINESensorConstants;
import spine.datamodel.Address;
import spine.datamodel.Data;
import spine.datamodel.Feature;
import spine.datamodel.FeatureData;
import spine.datamodel.HeartBeatData;
import spine.datamodel.MindsetData;
import spine.datamodel.ServiceMessage;
import spine.datamodel.ShimmerData;
import spine.datamodel.ZephyrData;
import spine.datamodel.functions.ShimmerNonSpineSetupSensor;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.PendingIntent;
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.pm.ActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.media.MediaPlayer;
import android.media.ToneGenerator;
import android.os.Bundle;
import android.os.IBinder;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
//Need the following import to get access to the app resources, since this
//class is in a sub-package.
import com.t2.R;
public class MeditationActivity extends BaseActivity
implements OnBioFeedbackMessageRecievedListener, SPINEListener,
View.OnTouchListener, SeekBar.OnSeekBarChangeListener,
AntPlusManager.Callbacks {
private static final String TAG = "BFDemo";
private static final String mActivityVersion = "2.4";
private static final int BLUETOOTH_SETTINGS_ID = 987;
private static final int HEARTRATE_SHIMMER = 1;
private static final int HEARTRATE_ZEPHYR = 2;
private static final int HEARTRATE_ANT = 3;
// int mHeartRateSource = HEARTRATE_SHIMMER; int mHeartRateSource = HEARTRATE_ZEPHYR;
int mHeartRateSource = HEARTRATE_ZEPHYR;
private String mAppId = "bioZenMeditation";
/**
* Flag to set for manual debugging of activity (Shows values on screen
*/
private boolean mDebug = false;
private int mIntroFade = 255;
private int mSubTimerClick = 100;
private String mUserId;
private String mSessionId;
Dao<BioUser, Integer> mBioUserDao;
Dao<BioSession, Integer> mBioSessionDao;
BioUser mCurrentBioUser = null;
BioSession mCurrentBioSession = null;
List<BioUser> currentUsers;
File mLogFile;
/**
* Number of seconds remaining in the session
* This is set initially from SharedPref.PREF_SESSION_LENGTH
*/
private int mSecondsRemaining = 0;
private int mSecondsTotal = 0;
/**
* Determines state of on screen button
* true = this is a start button
* false = this is a quit button
*/
private boolean mButtonIsStart = true;
/**
* Application version info determined by the package manager
*/
private String mApplicationVersion = "";
/**
* The Spine manager contains the bulk of the Spine server.
*/
private static SPINEManager mManager;
/**
* This is a broadcast receiver. Note that this is used ONLY for command/status messages from the AndroidBTService
* All data from the service goes through the mail SPINE mechanism (received(Data data)).
*/
private SpineReceiver mCommandReceiver;
/**
* Static mInstance of this activity
*/
private static MeditationActivity mInstance;
/**
* Toggled by screen press, indicates whether or not to show buttons/tools on screen
*/
private boolean mShowingControls = true;
/**
* Signal quality as reported by the mindset headset
* Value 0 - 200 0 is best, 199 is worst, 200 is no connection
*/
private int mSigQuality = 200;
private int mPrevSigQuality = 0;
private boolean mInternalSensorMonitoring = false;
/**
* Intent to start Big Brother service
*/
private PendingIntent mBigBrotherService;
private int mPollingPeriod = 30; // seconds
private int mSecondsWithoutActivityThreshold = 5; // seconds
private double mAccelerationThreshold = 12.0; // m/s^2
/**
* Timer for updating the UI
*/
private static Timer mDataUpdateTimer;
/**
* Class to help in saving received data to file
*/
private DataOutHandler mDataOutHandler;
/**
* Class to help in processing biometeric data
*/
private BioDataProcessor mBioDataProcessor = new BioDataProcessor(this);
private boolean mLoggingEnabled = true;
private boolean mLogCatEnabled = true;
private boolean mPaused = true;
private Boolean mBluetoothEnabled = false;
// UI Elements
private Button mToggleLogButton;
private Button mLlogMarkerButton;
private ImageButton mPauseButton;
private TextView mTextInfoView;
private TextView mTextViewInstructions;
private TextView mTextBioHarnessView;
private ImageView mBackgroundImage;
private ImageView mForegroundImage;
private ImageView mBaseImage;
private SeekBar mSeekBar;
private ImageView mSignalImage;
private ImageView mCountdownImageView;
private TextView mCountdownTextView;
private T2HeartRateDetector mHeartRateDetector = new T2HeartRateDetector();
private T2MovingAverageFilter mGroundLeadFilter = new T2MovingAverageFilter(64);
ChebyshevI mEcgBaselineFilter;
ChebyshevI mEcgNoiseFilter;
/**
* Moving average used to smooth the display of the band of interest
*/
private TMovingAverageFilter mMovingAverage;
private int mMovingAverageSize = 10;
private TMovingAverageFilter mMovingAverageROC;
private int mMovingAverageSizeROC = 6;
float maxMindsetValue = 0;
float minMindsetValue = 0;
float AverageMindsetValue = 0;
/**
* Gain used to determine how band of interest affects the background image
*/
private double mAlphaGain = 1;
protected SharedPreferences sharedPref;
private int mConfiguredGSRRange = ShimmerDevice.GSR_RANGE_HW_RES_3M3;
/**
* List of all BioParameters used in this activity
*/
private ArrayList<GraphBioParameter> mBioParameters = new ArrayList<GraphBioParameter>();
/**
* List of all currently PAIRED BioSensors
*/
private ArrayList<BioSensor> mBioSensors = new ArrayList<BioSensor>();
boolean mIsActive = false;
// We'll use these to get easy access to parameters in the mBioParameters array
private int eegPos;
private int gsrPos;
private int emgPos;
private int ecgPos;
private int heartRatePos;
private int respRatePos;
private int skinTempPos;
private int eHealthAirFlowPos;
private int eHealthTempPos;
private int eHealthSpO2Pos;
private int eHealthHeartRatePos;
private int eHealthGSRPos;
MindsetData currentMindsetData;
ZephyrData currentZephyrData = new ZephyrData();
private int mBackgroundControlParameter = MindsetData.THETA_ID; // Default to theta
private int mForegroundControlParameter = MindsetData.THETA_ID; // Default to theta
private int numSecsWithoutData = 0;
private String mAudioTrackResourceName;
private String mBaseImageResourceName;
private static Object mKeysLock = new Object();
private RateOfChange mRateOfChange;
private int mRateOfChangeSize = 6;
int mForegroundRawValue = 0;;
double mForegroundScaledValue = 0;;
int mForegroundFilteredValue = 0;;
private MediaPlayer mMediaPlayer;
private ToneGenerator mToneGenerator;
private boolean mShowForeground;
private boolean mShowToast;
/**
* Temp variable used in SelectUser() to indicate which user was selected
* Note that this needed to be a member variable because of error:
* "Cannot refer to a non-final variable mSelection inside an inner
* class defined in a different method"
*/
private int mSelection = 0;
boolean mSaveRawWave;
boolean mAllowComments;
boolean mShowAGain;
String[] mBioHarnessParameters;
String mLogFileName = "";
private Node mShimmerNode = null;
/**
* Node object for shimmer device as returned by spine
*/
public Node mSpineNode = null;
private boolean mDatabaseEnabled;
private boolean mAntHrmEnabled;
/**
* Static names dealing with the external database
*/
public static final String dDatabaseName = "";
public static final String dDesignDocName = "bigbrother-local";
public static final String dDesignDocId = "_design/" + dDesignDocName;
public static final String byDateViewName = "byDate";
/** Class to manage all the ANT messaging and setup */
private AntPlusManager mAntManager;
private boolean mAntServiceBound;
/** Shared preferences data filename. */
public static final String PREFS_NAME = "ANTDemo1Prefs";
/** Pair to any device. */
static final short ANT_WILDCARD = 0;
/** The default proximity search bin. */
private static final byte ANT_DEFAULT_BIN = 7;
/** The default event buffering buffer threshold. */
private static final short ANT_DEFAULT_BUFFER_THRESHOLD = 0;
/**
* Right now we're using only one shimmer node for all shimmer devices
* (since we address they by BT address)
* @return singleton for the shimmer node
*/
private Node getShimmerNode() {
if (mShimmerNode == null) {
mShimmerNode = new Node(new Address("" + Constants.RESERVED_ADDRESS_SHIMMER));
mManager.getActiveNodes().add(mShimmerNode);
}
return mShimmerNode;
}
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, this.getClass().getSimpleName() + ".onCreate()");
mInstance = this;
mRateOfChange = new RateOfChange(mRateOfChangeSize);
mIntroFade = 255;
// We don't want the screen to timeout in this activity
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
this.requestWindowFeature(Window.FEATURE_NO_TITLE); // This needs to happen BEFORE setContentView
setContentView(R.layout.buddah_activity_layout);
sharedPref = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
currentMindsetData = new MindsetData(this);
mSaveRawWave = SharedPref.getBoolean(this,
BioZenConstants.PREF_SAVE_RAW_WAVE,
BioZenConstants.PREF_SAVE_RAW_WAVE_DEFAULT);
mShowAGain = SharedPref.getBoolean(this,
BioZenConstants.PREF_SHOW_A_GAIN,
BioZenConstants.PREF_SHOW_A_GAIN_DEFAULT);
mAllowComments = SharedPref.getBoolean(this,
BioZenConstants.PREF_COMMENTS,
BioZenConstants.PREF_COMMENTS_DEFAULT);
mShowForeground = SharedPref.getBoolean(this,"show_lotus", true);
mShowToast = SharedPref.getBoolean(this,"show_toast", true);
mAudioTrackResourceName =SharedPref.getString(this, "audio_track" ,"None");
mBaseImageResourceName =SharedPref.getString(this, "background_images" ,"Sunset");
mBioHarnessParameters = getResources().getStringArray(R.array.bioharness_parameters_array);
setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
String s = SharedPref.getString(this, BioZenConstants.PREF_SESSION_LENGTH, "10");
mSecondsRemaining = Integer.parseInt(s) * 60;
mSecondsTotal = mSecondsRemaining;
s = SharedPref.getString(this, BioZenConstants.PREF_ALPHA_GAIN, "5");
mAlphaGain = Float.parseFloat(s);
mMovingAverage = new TMovingAverageFilter(mMovingAverageSize);
mMovingAverageROC = new TMovingAverageFilter(mMovingAverageSizeROC);
View v1 = findViewById (R.id.buddahView);
v1.setOnTouchListener (this);
Resources resources = this.getResources();
AssetManager assetManager = resources.getAssets();
// Set up member variables to UI Elements
mTextInfoView = (TextView) findViewById(R.id.textViewInfo);
mTextBioHarnessView = (TextView) findViewById(R.id.textViewBioHarness);
mCountdownTextView = (TextView) findViewById(R.id.countdownTextView);
mCountdownImageView = (ImageView) findViewById(R.id.imageViewCountdown);
mPauseButton = (ImageButton) findViewById(R.id.buttonPause);
mSignalImage = (ImageView) findViewById(R.id.imageView1);
mTextViewInstructions = (TextView) findViewById(R.id.textViewInstructions);
// Note that the seek bar is a debug thing - used only to set the
// alpha of the buddah image manually for visual testing
mSeekBar = (SeekBar)findViewById(R.id.seekBar1);
mSeekBar.setOnSeekBarChangeListener(this);
// Scale such that values to the right of center are scaled 1 - 10
// and values to the left of center are scaled 0 = .99999
if (mAlphaGain > 1.0) {
mSeekBar.setProgress(50 + (int) (mAlphaGain * 5));
}
else {
int i = (int) (mAlphaGain * 50);
mSeekBar.setProgress(i);
}
// mSeekBar.setProgress((int) mAlphaGain * 10);
// Controls start as invisible, need to touch screen to activate them
mCountdownTextView.setVisibility(View.INVISIBLE);
mCountdownImageView.setVisibility(View.INVISIBLE);
mTextInfoView.setVisibility(View.INVISIBLE);
mTextBioHarnessView.setVisibility(View.INVISIBLE);
mPauseButton.setVisibility(View.INVISIBLE);
mPauseButton.setVisibility(View.VISIBLE);
mSeekBar.setVisibility(View.INVISIBLE);
mBackgroundImage = (ImageView) findViewById(R.id.buddahView);
mForegroundImage = (ImageView) findViewById(R.id.lotusView);
mBaseImage = (ImageView) findViewById(R.id.backgroundView);
if (!mShowForeground) {
mForegroundImage.setVisibility(View.INVISIBLE);
}
int resource = 0;
if (mBaseImageResourceName.equalsIgnoreCase("Buddah")) {
mBackgroundImage.setImageResource(R.drawable.buddha);
mForegroundImage.setImageResource(R.drawable.lotus_flower);
mBaseImage.setImageResource(R.drawable.none_bg);
}
else if (mBaseImageResourceName.equalsIgnoreCase("Bob")) {
mBackgroundImage.setImageResource(R.drawable.bigbob);
mForegroundImage.setImageResource(R.drawable.red_nose);
mBaseImage.setImageResource(R.drawable.none_bg);
}
else if (mBaseImageResourceName.equalsIgnoreCase("Sunset")) {
mBackgroundImage.setImageResource(R.drawable.eeg_layer);
mForegroundImage.setImageResource(R.drawable.breathing_rate);
mBaseImage.setImageResource(R.drawable.meditation_bg);
}
// Initialize SPINE by passing the fileName with the configuration properties
try {
mManager = SPINEFactory.createSPINEManager("SPINETestApp.properties", resources);
} catch (InstantiationException e) {
Log.e(TAG, "Exception creating SPINE manager: " + e.toString());
e.printStackTrace();
}
// Since Mindset is a static node we have to manually put it in the active node list
// Note that the sensor id 0xfff1 (-15) is a reserved id for this particular sensor
Node mindsetNode = null;
mindsetNode = new Node(new Address("" + Constants.RESERVED_ADDRESS_MINDSET));
mManager.getActiveNodes().add(mindsetNode);
Node zepherNode = null;
zepherNode = new Node(new Address("" + Constants.RESERVED_ADDRESS_ZEPHYR));
mManager.getActiveNodes().add(zepherNode);
// The arduino node is programmed to look like a static Spine node
// Note that currently we don't have to turn it on or off - it's always streaming
// Since Spine (in this case) is a static node we have to manually put it in the active node list
// Since the
final int RESERVED_ADDRESS_ARDUINO_SPINE = 1; // 0x0001
mSpineNode = new Node(new Address("" + RESERVED_ADDRESS_ARDUINO_SPINE));
mManager.getActiveNodes().add(mSpineNode);
// Create a broadcast receiver. Note that this is used ONLY for command messages from the service
// All data from the service goes through the mail SPINE mechanism (received(Data data)).
// See public void received(Data data)
this.mCommandReceiver = new SpineReceiver(this);
try {
PackageManager packageManager = this.getPackageManager();
PackageInfo info = packageManager.getPackageInfo(this.getPackageName(), 0);
mApplicationVersion = info.versionName;
Log.i(TAG, "BioZen Application Version: " + mApplicationVersion + ", Activity Version: " + mActivityVersion);
}
catch (NameNotFoundException e) {
Log.e(TAG, e.toString());
}
// First create GraphBioParameters for each of the ECG static params (ie mindset)
int itemId = 0;
eegPos = itemId; // eeg always comes first
mBioParameters.clear();
for (itemId = 0; itemId < MindsetData.NUM_BANDS + 2; itemId++) { // 2 extra, for attention and meditation
GraphBioParameter param = new GraphBioParameter(itemId, MindsetData.spectralNames[itemId], "", true);
param.isShimmer = false;
mBioParameters.add(param);
}
// Now create all of the potential dynamic GBraphBioParameters (GSR, EMG, ECG, HR, Skin Temp, Resp Rate
String[] paramNamesStringArray = getResources().getStringArray(R.array.parameter_names);
for (String paramName: paramNamesStringArray) {
if (paramName.equalsIgnoreCase("not assigned"))
continue;
if (paramName.equalsIgnoreCase("EEG"))
continue;
GraphBioParameter param = new GraphBioParameter(itemId, paramName, "", true);
if (paramName.equalsIgnoreCase("gsr")) {
gsrPos = itemId;
param.isShimmer = true;
param.shimmerSensorConstant = SPINESensorConstants.SHIMMER_GSR_SENSOR;
param.shimmerNode = getShimmerNode();
}
if (paramName.equalsIgnoreCase("emg")) {
emgPos = itemId;
param.isShimmer = true;
param.shimmerSensorConstant = SPINESensorConstants.SHIMMER_EMG_SENSOR;
param.shimmerNode = getShimmerNode();
}
if (paramName.equalsIgnoreCase("ecg")) {
ecgPos = itemId;
param.isShimmer = true;
param.shimmerSensorConstant = SPINESensorConstants.SHIMMER_ECG_SENSOR;
param.shimmerNode = getShimmerNode();
}
if (paramName.equalsIgnoreCase("heart rate")) {
heartRatePos = itemId;
param.isShimmer = false;
}
if (paramName.equalsIgnoreCase("resp rate")) {
respRatePos = itemId;
param.isShimmer = false;
}
if (paramName.equalsIgnoreCase("skin temp")) {
skinTempPos = itemId;
param.isShimmer = false;
}
if (paramName.equalsIgnoreCase("EHealth Airflow")) {
eHealthAirFlowPos = itemId;
param.isShimmer = false;
}
if (paramName.equalsIgnoreCase("EHealth Temp")) {
eHealthTempPos = itemId;
param.isShimmer = false;
}
if (paramName.equalsIgnoreCase("EHealth SpO2")) {
eHealthSpO2Pos = itemId;
param.isShimmer = false;
}
if (paramName.equalsIgnoreCase("EHealth Heartrate")) {
eHealthHeartRatePos = itemId;
param.isShimmer = false;
}
if (paramName.equalsIgnoreCase("EHealth GSR")) {
eHealthGSRPos = itemId;
param.isShimmer = false;
}
itemId++;
mBioParameters.add(param);
}
// The session start time will be used as session id
// Note this also sets session start time
// **** This session ID will be prepended to all JSON data stored
// in the external database until it's changed (by the start
// of a new session.
Calendar cal = Calendar.getInstance();
SharedPref.setBioSessionId(sharedPref, cal.getTimeInMillis());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss", Locale.US);
String sessionDate = sdf.format(new Date());
long sessionId = SharedPref.getLong(this, "bio_session_start_time", 0);
mUserId = SharedPref.getString(this, "SelectedUser", "");
// Now get the database object associated with this user
try {
mBioUserDao = getHelper().getBioUserDao();
mBioSessionDao = getHelper().getBioSessionDao();
QueryBuilder<BioUser, Integer> builder = mBioUserDao.queryBuilder();
builder.where().eq(BioUser.NAME_FIELD_NAME, mUserId);
builder.limit(1);
// builder.orderBy(ClickCount.DATE_FIELD_NAME, false).limit(30);
List<BioUser> list = mBioUserDao.query(builder.prepare());
if (list.size() == 1) {
mCurrentBioUser = list.get(0);
}
else if (list.size() == 0)
{
try {
mCurrentBioUser = new BioUser(mUserId, System.currentTimeMillis());
mBioUserDao.create(mCurrentBioUser);
} catch (SQLException e1) {
Log.e(TAG, "Error creating user " + mUserId , e1);
}
}
else {
Log.e(TAG, "General Database error" + mUserId);
}
} catch (SQLException e) {
Log.e(TAG, "Can't find user: " + mUserId , e);
}
mSignalImage.setImageResource(R.drawable.signal_bars0);
// Check to see of there a device configured for EEG, if so then show the skin conductance meter
String tmp = SharedPref.getString(this, "EEG" ,null);
if (tmp != null) {
mSignalImage.setVisibility(View.VISIBLE);
mTextViewInstructions.setVisibility(View.VISIBLE);
}
else {
mSignalImage.setVisibility(View.INVISIBLE);
mTextViewInstructions.setVisibility(View.INVISIBLE);
}
mDataOutHandler = new DataOutHandler(this, mUserId,sessionDate, mAppId, DataOutHandler.DATA_TYPE_EXTERNAL_SENSOR, sessionId );
if (mDatabaseEnabled) {
TelephonyManager telephonyManager = (TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE);
String myNumber = telephonyManager.getLine1Number();
String remoteDatabaseUri = SharedPref.getString(this, "database_sync_name", getString(R.string.database_uri));
// remoteDatabaseUri += myNumber;
Log.d(TAG, "Initializing database at " + remoteDatabaseUri); // TODO: remove
try {
mDataOutHandler.initializeDatabase(dDatabaseName, dDesignDocName, dDesignDocId, byDateViewName, remoteDatabaseUri);
} catch (DataOutHandlerException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
}
mDataOutHandler.setRequiresAuthentication(false);
}
mBioDataProcessor.initialize(mDataOutHandler);
mLoggingEnabled = SharedPref.getBoolean(this, "enable_logging", true);
mDatabaseEnabled = SharedPref.getBoolean(this, "database_enabled", false);
mAntHrmEnabled = SharedPref.getBoolean(this, "enable_ant_hrm", false);
mInternalSensorMonitoring = SharedPref.getBoolean(this, "inernal_sensor_monitoring_enabled", false);
if (mAntHrmEnabled) {
mHeartRateSource = HEARTRATE_ANT;
}
else {
mHeartRateSource = HEARTRATE_ZEPHYR;
}
if (mLoggingEnabled) {
mDataOutHandler.enableLogging(this);
}
if (mLogCatEnabled) {
mDataOutHandler.enableLogCat();
}
// Log the version
try {
PackageManager packageManager = getPackageManager();
PackageInfo info = packageManager.getPackageInfo(getPackageName(), 0);
mApplicationVersion = info.versionName;
String versionString = mAppId + " application version: " + mApplicationVersion;
DataOutPacket packet = new DataOutPacket();
packet.add(DataOutHandlerTags.version, versionString);
try {
mDataOutHandler.handleDataOut(packet);
} catch (DataOutHandlerException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
}
}
catch (NameNotFoundException e) {
Log.e(TAG, e.toString());
}
if (mInternalSensorMonitoring) {
// IntentSender Launches our service scheduled with with the alarm manager
mBigBrotherService = PendingIntent.getService(MeditationActivity.this,
0, new Intent(MeditationActivity.this, BigBrotherService.class), 0);
long firstTime = SystemClock.elapsedRealtime();
// Schedule the alarm!
AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);
am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
firstTime, mPollingPeriod * 1000, mBigBrotherService);
// Tell the user about what we did.
Toast.makeText(MeditationActivity.this, R.string.service_scheduled,
Toast.LENGTH_LONG).show();
}
} // End onCreate(Bundle savedInstanceState)
@Override
public void onBackPressed() {
handlePause("Session Complete"); // Allow opportinuty for a note
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.i(TAG, this.getClass().getSimpleName() + ".onDestroy()");
if (mInternalSensorMonitoring) {
// And cancel the alarm.
AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);
am.cancel(mBigBrotherService);
Intent intent = new Intent();
intent.setAction(BigBrotherConstants.ACTION_COMMAND_BROADCAST);
intent.putExtra("message", BigBrotherConstants.SERVICE_OFF);
sendBroadcast(intent);
// Tell the user about what we did.
Toast.makeText(MeditationActivity.this, R.string.service_unscheduled,
Toast.LENGTH_LONG).show();
}
// Send stop command to every shimmer device
// You might think that it would be better to iterate through the mBioSensors table
// instead of the mBioParameters table but it's actually easier this way
for (GraphBioParameter param : mBioParameters) {
if (param.isShimmer && param.shimmerNode != null) {
ShimmerNonSpineSetupSensor setup = new ShimmerNonSpineSetupSensor();
setup.setSensor(param.shimmerSensorConstant);
String deviceAddress = SharedPref.getDeviceForParam(this, param.title1);
if (deviceAddress != null) {
setup.setBtAddress(Util.AsciiBTAddressToBytes(deviceAddress));
setup.setCommand(ShimmerNonSpineSetupSensor.SHIMMER_COMMAND_STOPPED);
Log.i(TAG, String.format("Setting up Shimmer sensor: %s (%s) (%d) SHIMMER_COMMAND_STOPPED",
param.shimmerNode.getPhysicalID(), deviceAddress, param.shimmerSensorConstant));
mManager.setup(param.shimmerNode, setup);
}
}
}
if (mMediaPlayer != null) {
mMediaPlayer.stop();
mMediaPlayer.release();
mMediaPlayer = null;
}
this.unregisterReceiver(this.mCommandReceiver);
mDataOutHandler.close();
} // End onDestroy()
@Override
protected void onStart() {
super.onStart();
mIsActive = true;
Log.i(TAG, this.getClass().getSimpleName() + ".onStart()");
// mPauseButton.setText("Start");
mPauseButton.setImageResource(R.drawable.start);
// Set up filter intents so we can receive broadcasts
IntentFilter filter = new IntentFilter();
filter.addAction("com.t2.biofeedback.service.status.BROADCAST");
this.registerReceiver(this.mCommandReceiver,filter);
// Set up a timer to do graphical updates
mDataUpdateTimer = new Timer();
mDataUpdateTimer.schedule(new TimerTask() {
@Override
public void run() {
TimerMethod();
}
}, 0, 10);
if (mMediaPlayer != null) {
mMediaPlayer.stop();
}
int resource = 0;
if (mAudioTrackResourceName.contains("Air Synth")) resource = R.raw.dave_luxton_air_synth_meditation;
if (mAudioTrackResourceName.contains("Entity and Echo")) resource = R.raw.dave_luxton_entity_and_echo_meditation;
if (mAudioTrackResourceName.contains("Starlit Lake")) resource = R.raw.dave_luxton_starlit_lake_meditation;
if (resource != 0) {
mMediaPlayer = MediaPlayer.create(this, resource);
if (mMediaPlayer != null) {
mMediaPlayer.start();
mMediaPlayer.setLooping(true);
}
}
if (mAntHrmEnabled) {
mAntServiceBound = bindService(new Intent(this, ANTPlusService.class), mConnection, BIND_AUTO_CREATE);
}
}
/**
* Convert seconds to string display of hours:minutes:seconds
* @param time Total number of seconds to display
* @return String formated to hours:minutes:seconds
*/
String secsToHMS(long time) {
long secs = time;
long hours = secs / 3600;
secs = secs % 3600;
long mins = secs / 60;
secs = secs % 60;
return hours + ":" + mins + ":" + secs;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// this.getMenuInflater().inflate(R.menu.menu_compassion_meditation, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case R.id.settings:
Intent intent2 = new Intent(this, BTServiceManager.class);
this.startActivity(intent2);
return true;
case R.id.discover:
mManager.discoveryWsn();
return true;
case R.id.about:
String content = "National Center for Telehealth and Technology (T2)\n\n";
content += "BioZen Application\n";
content += "Application Version: " + mApplicationVersion + "\n";
content += "Activity Version: " + mActivityVersion;
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setTitle("About");
alert.setMessage(content);
alert.show();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
/**
* This callback is called whenever the AndroidBTService sends us an indication that
* it is actively trying to establish a BT connection to one of the nodes.
*
* @see com.t2.SpineReceiver.OnBioFeedbackMessageRecievedListener#onStatusReceived(com.t2.SpineReceiver.BioFeedbackStatus)
*/
@Override
public void onStatusReceived(BioFeedbackStatus bfs) {
String name = bfs.name;
if (name == null ) name = "sensor node";
if(bfs.messageId.equals("CONN_CONNECTING")) {
Log.i(TAG, "Received command : " + bfs.messageId + " to " + name );
if (mShowToast)
Toast.makeText (getApplicationContext(), "**** Connecting to Sensor Node ****", Toast.LENGTH_SHORT).show ();
}
else if(bfs.messageId.equals("CONN_ANY_CONNECTED")) {
Log.i(TAG, "Received command : " + bfs.messageId + " to " + name );
// Something has connected - discover what it was
mManager.discoveryWsn();
if (mShowToast)
Toast.makeText (getApplicationContext(), "**** Sensor Node Connected ****", Toast.LENGTH_SHORT).show ();
}
else if(bfs.messageId.equals("CONN_CONNECTION_LOST")) {
Log.i(TAG, "Received command : " + bfs.messageId + " to " + name );
if (mShowToast)
Toast.makeText (getApplicationContext(), "**** Sensor Node Connection lost ****", Toast.LENGTH_SHORT).show ();
}
else if(bfs.messageId.equals("STATUS_PAIRED_DEVICES")) {
Log.i(TAG, "Received command : " + bfs.messageId + " to " + name );
Log.i(TAG, bfs.address );
// We don't want to take any action unless we're ready to go
if (!mIsActive)
return;
populateBioSensors(bfs.address);
validateBioSensors();
// Send startup command to every shimmer device
for (GraphBioParameter param : mBioParameters) {
if (param.isShimmer && param.shimmerNode != null) {
ShimmerNonSpineSetupSensor setup = new ShimmerNonSpineSetupSensor();
setup.setSensor(param.shimmerSensorConstant);
String deviceAddress = SharedPref.getDeviceForParam(this, param.title1);
if (deviceAddress != null) {
setup.setBtAddress(Util.AsciiBTAddressToBytes(deviceAddress));
// byte startShimmercommand = ShimmerNonSpineSetupSensor.SHIMMER_COMMAND_RUNNING_4HZ_3M3;
byte startShimmercommand = ShimmerNonSpineSetupSensor.SHIMMER_COMMAND_RUNNING_4HZ_AUTORANGE;
setup.setCommand(startShimmercommand);
mConfiguredGSRRange = Util.getGsrRangeFromShimmerCommand(startShimmercommand);
Log.i(TAG, String.format("Setting up Shimmer sensor: %s (%s) (%d) SHIMMER_COMMAND_RUNNING",
param.shimmerNode.getPhysicalID(), deviceAddress, param.shimmerSensorConstant));
mManager.setup(param.shimmerNode, setup);
}
else {
}
}
}
}
}
@Override
public void newNodeDiscovered(Node newNode) {
}
@Override
public void received(ServiceMessage msg) {
}
/**
* This is where we receive sensor data that comes through the actual
* Spine channel.
* @param data Generic Spine data packet. Should be cast to specifid data type indicated by data.getFunctionCode()
*
* @see spine.SPINEListener#received(spine.datamodel.Data)
*/
@Override
public void received(Data data) {
if (data != null) {
switch (data.getFunctionCode()) {
// E-Health board
case SPINEFunctionConstants.FEATURE: {
FeatureData featureData = (FeatureData) data;
Feature[] feats = featureData.getFeatures();
if (feats.length < 2) {
break;
}
Feature firsFeat = feats[0];
Feature Feat2 = feats[1];
int airFlow = firsFeat.getCh1Value();
int scaledTemp = firsFeat.getCh2Value();
float temp = (float)scaledTemp/(65535F/9F) + 29F;
int BPM = firsFeat.getCh3Value();
int SPO2 = firsFeat.getCh4Value();
int scaledConductance = Feat2.getCh1Value();
float conductance = (float) scaledConductance / (65535F/4F);
Log.d(TAG, "E-health Values = " + airFlow + ", " + temp + ", " + BPM + ", " + SPO2 + ", " + conductance + ", " );
synchronized(mKeysLock) {
mBioParameters.get(eHealthAirFlowPos).rawValue = airFlow;
mBioParameters.get(eHealthAirFlowPos).setScaledValue((int)((double) map(airFlow,0,360,0,255) * mAlphaGain));
mBioParameters.get(eHealthTempPos).rawValue = (int) temp;
mBioParameters.get(eHealthTempPos).setScaledValue((int)((double) map(temp,29,40,0,255) * mAlphaGain));
mBioParameters.get(eHealthHeartRatePos).rawValue = BPM;
mBioParameters.get(eHealthHeartRatePos).setScaledValue((int)((double) map(BPM,30,220,0,255) * mAlphaGain));
mBioParameters.get(eHealthSpO2Pos).rawValue = SPO2;
mBioParameters.get(eHealthSpO2Pos).setScaledValue((int)((double) map(SPO2,0,100,0,255) * mAlphaGain));
mBioParameters.get(eHealthGSRPos).rawValue = (int) map(scaledConductance,0,65535,0,100);
mBioParameters.get(eHealthGSRPos).setScaledValue((int)((double) map(scaledConductance,0,65535,0,255) * mAlphaGain));
DataOutPacket packet = new DataOutPacket();
packet.add(DataOutHandlerTags.RAW_HEARTRATE, BPM);
packet.add(DataOutHandlerTags.RAW_GSR, conductance);
packet.add(DataOutHandlerTags.RAW_SKINTEMP, temp);
packet.add(DataOutHandlerTags.SPO2, SPO2);
packet.add(DataOutHandlerTags.AIRFLOW, airFlow);
try {
mDataOutHandler.handleDataOut(packet);
} catch (DataOutHandlerException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
}
}
break;
}
case SPINEFunctionConstants.HEARTBEAT: {
synchronized(mKeysLock) {
HeartBeatData thisData = (HeartBeatData) data;
int scaled = (thisData.getBPM() )/2 ;
if (mHeartRateSource == HEARTRATE_ANT) {
mBioParameters.get(heartRatePos).rawValue = thisData.getBPM();
mBioParameters.get(heartRatePos).scaledValue = scaled;
}
// Send data to output
DataOutPacket packet = new DataOutPacket();
packet.add(DataOutHandlerTags.RAW_HEARTRATE, thisData.getBPM());
try {
mDataOutHandler.handleDataOut(packet);
} catch (DataOutHandlerException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
}
}
break;
}
case SPINEFunctionConstants.SHIMMER: {
Node node = data.getNode();
numSecsWithoutData = 0;
Node source = data.getNode();
ShimmerData shimmerData = (ShimmerData) data;
switch (shimmerData.sensorCode) {
case SPINESensorConstants.SHIMMER_GSR_SENSOR:
mBioDataProcessor.processShimmerGSRData(shimmerData, mConfiguredGSRRange);
double scaled = MathExtra.scaleData((float)mBioDataProcessor.mGsrResistance, 2000000F, 0F, 255) * mAlphaGain;
synchronized(mKeysLock) {
mBioParameters.get(gsrPos).rawValue = (int) scaled;
mBioParameters.get(gsrPos).setScaledValue((int) scaled);
}
break;
case SPINESensorConstants.SHIMMER_EMG_SENSOR:
mBioDataProcessor.processShimmerEMGData(shimmerData);
scaled = MathExtra.scaleData((float)shimmerData.emg, 4000F, 0F, 255) * mAlphaGain;
synchronized(mKeysLock) {
mBioParameters.get(emgPos).rawValue = (int) scaled;
mBioParameters.get(emgPos).setScaledValue((int) scaled);
}
break;
case SPINESensorConstants.SHIMMER_ECG_SENSOR:
// If we're receiving packets from shimmer egg then swith the heartrate to shimmer
// Otherwise we'll leave it at the default which is zephyr
mHeartRateSource = HEARTRATE_SHIMMER;
mBioDataProcessor.processShimmerECGData(shimmerData);
scaled = MathExtra.scaleData((float)shimmerData.ecg, 4000F, 0F, 255) * mAlphaGain;
synchronized(mKeysLock) {
mBioParameters.get(ecgPos).rawValue = (int) scaled;
mBioParameters.get(ecgPos).setScaledValue((int) scaled);
}
break;
} // End switch (shimmerData.sensorCode)
break;
}
case SPINEFunctionConstants.ZEPHYR: {
mBioDataProcessor.processZephyrData(data);
Node source = data.getNode();
Feature[] feats = ((FeatureData)data).getFeatures();
Feature firsFeat = feats[0];
currentZephyrData.heartRate = mBioDataProcessor.mZephyrHeartRate;
currentZephyrData.respRate = (int) mBioDataProcessor.mRespRate;
currentZephyrData.skinTemp = (int) mBioDataProcessor.mSkinTempF;
synchronized(mKeysLock) {
double scaled = MathExtra.scaleData((float)mBioDataProcessor.mSkinTempF, 110F, 70F, 255) * mAlphaGain;
mBioParameters.get(skinTempPos).rawValue = (int) mBioDataProcessor.mSkinTempF;
mBioParameters.get(skinTempPos).setScaledValue((int) scaled);
scaled = MathExtra.scaleData((float)mBioDataProcessor.mZephyrHeartRate, 250F, 20F, 255) * mAlphaGain;
mBioParameters.get(heartRatePos).rawValue = mBioDataProcessor.mZephyrHeartRate;
mBioParameters.get(heartRatePos).setScaledValue((int) scaled);
scaled = MathExtra.scaleData((float)mBioDataProcessor.mRespRate, 120F, 5F, 255) * mAlphaGain;
mBioParameters.get(respRatePos).rawValue = (int) mBioDataProcessor.mRespRate;
mBioParameters.get(respRatePos).setScaledValue((int) scaled);
}
numSecsWithoutData = 0;
break;
} // End case SPINEFunctionConstants.ZEPHYR:
case SPINEFunctionConstants.MINDSET: {
Node source = data.getNode();
MindsetData mindsetData = (MindsetData) data;
mBioDataProcessor.processMindsetData(data, currentMindsetData);
if (mindsetData.exeCode == Constants.EXECODE_POOR_SIG_QUALITY) {
synchronized(mKeysLock) {
currentMindsetData.poorSignalStrength = mindsetData.poorSignalStrength;
}
mSigQuality = mindsetData.poorSignalStrength & 0xff;
if (mShowingControls || mSigQuality == 200)
mSignalImage.setVisibility(View.VISIBLE);
else
mSignalImage.setVisibility(View.INVISIBLE);
if (mSigQuality == 200)
mSignalImage.setImageResource(R.drawable.signal_bars0);
else if (mSigQuality > 150)
mSignalImage.setImageResource(R.drawable.signal_bars1);
else if (mSigQuality > 100)
mSignalImage.setImageResource(R.drawable.signal_bars2);
else if (mSigQuality > 50)
mSignalImage.setImageResource(R.drawable.signal_bars3);
else if (mSigQuality > 25)
mSignalImage.setImageResource(R.drawable.signal_bars4);
else
mSignalImage.setImageResource(R.drawable.signal_bars5);
if (mSigQuality == 200 && mPrevSigQuality != 200) {
Toast.makeText (getApplicationContext(), "Headset not makeing good skin contact. Please Adjust", Toast.LENGTH_LONG).show ();
}
mPrevSigQuality = mSigQuality;
}
if (mindsetData.exeCode == Constants.EXECODE_SPECTRAL || mindsetData.exeCode == Constants.EXECODE_RAW_ACCUM) {
synchronized(mKeysLock) {
if (mPaused == false) {
numSecsWithoutData = 0;
synchronized(mKeysLock) {
for (int i = 0; i < MindsetData.NUM_BANDS + 2; i++) { // 2 extra, for attention and meditation
// float scaled = MathExtra.scaleData((float)currentMindsetData.getFeatureValue(i), 100F, 20F, 255, (float)mAlphaGain);
float scaled = MathExtra.scaleData((float)currentMindsetData.getFeatureValue(i), 100F, 20F, 255);
scaled *= mAlphaGain;
if (scaled > 255)
scaled = 255;
mBioParameters.get(i).rawValue = currentMindsetData.getFeatureValue(i);
mBioParameters.get(i).setScaledValue((int) scaled);
}
}
} // End if (mPaused == false)
}
}
break;
} // End case SPINEFunctionConstants.MINDSET:
} // End switch (data.getFunctionCode())
} // End if (data != null)
}
@Override
public void discoveryCompleted(Vector activeNodes) {
Log.d(TAG, this.getClass().getSimpleName() + ".discoveryCompleted()");
// Tell the bluetooth service to send us a list of bluetooth devices and system status
// Response comes in public void onStatusReceived(BioFeedbackStatus bfs) STATUS_PAIRED_DEVICES
mManager.pollBluetoothDevices();
}
/**
* Converts a byte array to an integer
* @param bytes Bytes to convert
* @return Integer representaion of byte array
*/
public static int byteArrayToInt(byte[] bytes) {
int val = 0;
for(int i = 0; i < bytes.length; i++) {
int n = (bytes[i] < 0 ? (int)bytes[i] + 256 : (int)bytes[i]) << (8 * i);
val += n;
}
return val;
}
/**
* Hansles UI button clicks
* @param v
*/
public void onButtonClick(View v)
{
final int id = v.getId();
switch (id) {
case R.id.buttonBack:
finish();
break;
case R.id.buttonPause:
if (mPaused) {
mPaused = false;
mPauseButton.setImageResource(R.drawable.quit);
mTextViewInstructions.setVisibility(View.INVISIBLE);
toggleControls();
Toast.makeText(mInstance, "You may toggle the screen controls back \non by pressing anywhere on the screen", Toast.LENGTH_SHORT).show();
}
else {
handlePause(mUserId + mSessionId + " Paused");
}
break;
} // End switch
}
/**
* This method is called directly by the timer and runs in the same thread as the timer
* From here We call the method that will work with the UI through the runOnUiThread method.
*/
private void TimerMethod()
{
this.runOnUiThread(Timer_Tick);
}
/**
* This method runs in the same thread as the UI.
*/
private Runnable Timer_Tick = new Runnable() {
public void run() {
// We get here every .01 second
if (mPaused == true || currentMindsetData == null || currentZephyrData == null) {
return;
}
if (mSubTimerClick-- > 0) {
if (mIntroFade > 0) {
mBackgroundImage.setAlpha(mIntroFade--);
mForegroundImage.setAlpha(mIntroFade--);
}
return;
}
else {
mSubTimerClick = 100;
}
// We get here every 1 second
numSecsWithoutData++;
if (mLoggingEnabled == true && numSecsWithoutData < 2) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
String currentDateTimeString = DateFormat.getDateInstance().format(new Date());
currentDateTimeString = sdf.format(new Date());
String logData = currentDateTimeString + ", " + currentZephyrData.getLogDataLine();
logData += currentMindsetData.getLogDataLine(currentMindsetData.exeCode, mSaveRawWave) + "\n";
}
// Background parameters
// NOTE that this is a huge hack (and needs to be fixed.
// mBackgroundControlParameter (which comes from preferences, is based on the list R.array.bands_of_interest_array
// which FOR NOW, matches the assignments here when assigning mBioParameters itemID's.
// These all must be changed to use the same list
// To further complicate the matter mBioParameters itemID's are assigned based on MindsetData.NUM_BANDS
// AND R.array.parameter_names
GraphBioParameter backgroundParam = mBioParameters.get(mBackgroundControlParameter);
String backgroundLogText = String.format("Background: %s (raw, scaled, filtered): %d, %d, %d", backgroundParam.title1, backgroundParam.rawValue, backgroundParam.scaledValue, backgroundParam.getFilteredScaledValue());
mTextInfoView.setText(backgroundLogText);
Log.d(TAG, backgroundLogText);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
String currentDateTimeString = DateFormat.getDateInstance().format(new Date());
currentDateTimeString = sdf.format(new Date());
try {
mDataOutHandler.logNote(currentDateTimeString + ", " + currentDateTimeString);
} catch (DataOutHandlerException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
}
// Foreground parameters
GraphBioParameter foregroundParam = mBioParameters.get(mForegroundControlParameter);
mForegroundRawValue = foregroundParam.rawValue;
// int iBackgroundAlphaValue = (int) ((double) backgroundParam.getFilteredScaledValue() * mAlphaGain);
int iBackgroundAlphaValue = backgroundParam.getFilteredScaledValue();
// We want to update the rate of change once every second
foregroundParam.updateRateOfChange();
int iForegroundAlphaValue = 255 - foregroundParam.getRateOfChangeScaledValue();
String foregroundLogText = String.format("Foreground: %s (scaled, ROC, ALPHA): %d, %d %d", foregroundParam.title1, backgroundParam.getFilteredScaledValue(), foregroundParam.getRateOfChangeScaledValue(), iForegroundAlphaValue);
mTextBioHarnessView.setText(foregroundLogText);
// Log.d(TAG, foregroundLogText);
if (mIntroFade <= 0) {
mBackgroundImage.setAlpha(iBackgroundAlphaValue);
if (mShowForeground) {
mForegroundImage.setAlpha(iForegroundAlphaValue);
}
else {
mForegroundImage.setAlpha(0);
}
}
if (mSecondsRemaining-- > 0) {
mCountdownTextView.setText(secsToHMS(mSecondsRemaining));
}
else {
if (mMediaPlayer != null) {
mMediaPlayer.stop();
}
mMediaPlayer = MediaPlayer.create(mInstance, R.raw.wind_chime_1);
if (mMediaPlayer != null) {
mMediaPlayer.start();
mMediaPlayer.setLooping(true);
}
handlePause("Session Complete"); // Allow opportinuty for a note
}
}
};
@Override
protected void onPause() {
Log.i(TAG, this.getClass().getSimpleName() + ".onPause()");
mIsActive = false;
// *******************
// Make sure to to this or else you will get more and more notifications from Spine as you
// go into and out of activities!
// Also make sure to do this in on pause (as opposed to onStop or ondestroy.
// This will prevent you from receiving messages possibly requested by another activity
mManager.removeListener(this);
mDataUpdateTimer.purge();
mDataUpdateTimer.cancel();
currentMindsetData.saveScaleData();
super.onPause();
}
@Override
protected void onStop() {
Log.i(TAG, this.getClass().getSimpleName() + ".onStop()");
if(mAntManager != null)
{
saveAntState();
mAntManager.setCallbacks(null);
if (mAntManager.isChannelOpen(AntPlusManager.HRM_CHANNEL))
{
Log.d(TAG, "onClick (HRM): Close channel");
mAntManager.closeChannel(AntPlusManager.HRM_CHANNEL);
}
}
if(mAntServiceBound)
{
unbindService(mConnection);
}
super.onStop();
}
@Override
protected void onRestart() {
Log.i(TAG, this.getClass().getSimpleName() + ".onRestart()");
super.onRestart();
}
@Override
protected void onResume() {
Log.i(TAG, this.getClass().getSimpleName() + ".onResume()");
restoreState();
// ... then we need to register a SPINEListener implementation to the SPINE manager mInstance
// to receive sensor node data from the Spine server
// (I register myself since I'm a SPINEListener implementation!)
mManager.addListener(this);
mManager.discoveryWsn(); // discoveryCompleted() is called after this is done
super.onResume();
}
void restoreState()
{
String s = SharedPref.getString(this, BioZenConstants.PREF_BAND_OF_INTEREST ,"0");
mBackgroundControlParameter = Integer.parseInt(s);
s = SharedPref.getString(this,
BioZenConstants.PREF_BIOHARNESS_PARAMETER_OF_INTEREST ,
BioZenConstants.PREF_BIOHARNESS_PARAMETER_OF_INTEREST_DEFAULT);
mForegroundControlParameter = Integer.parseInt(s);
}
void toggleControls() {
// Toggle showing screen buttons/controls
if (mShowingControls) {
mShowingControls = false;
mCountdownImageView.setVisibility(View.INVISIBLE);
mCountdownTextView.setVisibility(View.INVISIBLE);
mTextInfoView.setVisibility(View.INVISIBLE);
mTextBioHarnessView.setVisibility(View.INVISIBLE);
mPauseButton.setVisibility(View.INVISIBLE);
mSeekBar.setVisibility(View.INVISIBLE);
}
else {
mShowingControls = true;
mCountdownImageView.setVisibility(View.VISIBLE);
mCountdownTextView.setVisibility(View.VISIBLE);
if (mDebug) mTextInfoView.setVisibility(View.VISIBLE);
if (mDebug) mTextBioHarnessView.setVisibility(View.VISIBLE);
mPauseButton.setVisibility(View.VISIBLE);
mSeekBar.setVisibility(mShowAGain ? View.VISIBLE :View.INVISIBLE);
}
}
@Override
public boolean onTouch(View arg0, MotionEvent arg1) {
if (!mPaused) toggleControls();
return false;
}
@Override
public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) {
// mAlphaGain = arg1/10;
// if (mAlphaGain <= 0) mAlphaGain = 1;
// Scale such that values to the right of center are scaled 1 - 10
// and values to the left of center are scaled 0 = .99999
if (arg1 > 50) {
float farg1 = (float) arg1;
mAlphaGain = (farg1 - 50) / 5;
}
else {
mAlphaGain = (float) arg1 / (float) 50;
}
}
@Override
public void onStartTrackingTouch(SeekBar arg0) {
}
@Override
public void onStopTrackingTouch(SeekBar arg0) {
SharedPref.putString(this,
BioZenConstants.PREF_ALPHA_GAIN, new Float(mAlphaGain).toString() );
Toast.makeText(this, " AlphaGain changed to " + mAlphaGain, Toast.LENGTH_SHORT).show();
}
/**
* Handles the pause button press
* Brings up a dialog that allows the user to either restart, or quit
* Note that in any case the text entered by the user is saved to the log file
*/
public void handlePause(String message) {
mPaused = true;
// if (mMediaPlayer != null) {
// mMediaPlayer.pause();
// }
Intent intent1 = new Intent(mInstance, EndSessionActivity.class);
mInstance.startActivityForResult(intent1, BioZenConstants.END_SESSION_ACTIVITY);
}
/**
* Writes a specific note to the log - adding a time stamp
* @param note Note to save to log
*/
void addNoteToLog(String note) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
String currentDateTimeString = DateFormat.getDateInstance().format(new Date());
currentDateTimeString = sdf.format(new Date());
try {
mDataOutHandler.logNote(currentDateTimeString + ", " + note + "\n");
} catch (DataOutHandlerException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch(requestCode) {
case BioZenConstants.END_SESSION_ACTIVITY:
if (data != null) {
int action = data.getIntExtra(
BioZenConstants.END_SESSION_ACTIVITY_RESULT,
BioZenConstants.END_SESSION_RESTART);
switch (action) {
default:
case BioZenConstants.END_SESSION_RESTART:
break;
case BioZenConstants.END_SESSION_SAVE:
EndAndSaveSession(data);
break;
case BioZenConstants.END_SESSION_QUIT:
if (mLogFile != null)
mLogFile.delete();
Analytics.onEndSession(this);
finish();
break;
}
}
else {
if (mLogFile != null)
mLogFile.delete();
Analytics.onEndSession(this);
finish();
}
break;
}
}
void EndAndSaveSession(Intent data) {
String notes = "";
String categoryName = "";
if (data != null) {
notes = data.getStringExtra(
BioZenConstants.END_SESSION_ACTIVITY_NOTES);
categoryName = data.getStringExtra(
BioZenConstants.END_SESSION_ACTIVITY_CATEGORY);
if (categoryName == null) categoryName = "";
if (notes == null) notes = "";
}
addNoteToLog(notes);
// -----------------------------
// Save stats for session
// -----------------------------
// Create a session data point for this session (to put in data
mCurrentBioSession = new BioSession(mCurrentBioUser, System.currentTimeMillis());
if (mCurrentBioSession != null) {
mCurrentBioSession.comments += notes;
mCurrentBioSession.category = categoryName;
for (int i = 0; i < BioZenConstants.MAX_KEY_ITEMS; i++) {
mCurrentBioSession.maxFilteredValue[i] = mBioParameters.get(i).getMaxFilteredValue();
mCurrentBioSession.minFilteredValue[i] =
mBioParameters.get(i).getMinFilteredValue() != 9999 ? mBioParameters.get(i).getMinFilteredValue() : 0;
mCurrentBioSession.avgFilteredValue[i] = mBioParameters.get(i).getAvgFilteredValue();
mCurrentBioSession.keyItemNames[i] = mBioParameters.get(i).title1;
}
int secondsCompleted = mSecondsTotal - mSecondsRemaining;
float precentComplete = (float) secondsCompleted / (float) mSecondsTotal;
mCurrentBioSession.precentComplete = (int) (precentComplete * 100);
mCurrentBioSession.secondsCompleted = secondsCompleted;
mCurrentBioSession.logFileName = mLogFileName;
mCurrentBioSession.mindsetBandOfInterestIndex = mBackgroundControlParameter;
mCurrentBioSession.bioHarnessParameterOfInterestIndex = mForegroundControlParameter;
// Udpate the database with the current session
try {
mBioSessionDao.create(mCurrentBioSession);
} catch (SQLException e1) {
Log.e(TAG, "Error saving current session to database", e1);
}
}
finish();
}
/**
* Receives a json string containing data about all of the paired sensors
* the adds a new BioSensor for each one to the mBioSensors collection
*
* @param jsonString String containing info on all paired devices
*/
private void populateBioSensors(String jsonString) {
Log.d(TAG, this.getClass().getSimpleName() + " populateBioSensors");
// Now clear it out and populate it. The only difference is that
// if a sensor previously existed, then
mBioSensors.clear();
try {
JSONArray jsonArray = new JSONArray(jsonString);
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject jsonObject = jsonArray.getJSONObject(i);
Boolean enabled = jsonObject.getBoolean("enabled");
String name = jsonObject.getString("name");
String address = jsonObject.getString("address");
int connectionStatus = jsonObject.getInt("connectionStatus");
if (name.equalsIgnoreCase("system")) {
mBluetoothEnabled = enabled;
}
else {
Log.i(TAG, "Adding sensor " + name + ", " + address + (enabled ? ", enabled":", disabled") + " : " + Util.connectionStatusToString(connectionStatus));
BioSensor bioSensor = new BioSensor(name, address, enabled);
bioSensor.mConnectionStatus = connectionStatus;
mBioSensors.add(bioSensor);
}
}
} catch (JSONException e) {
Log.e(TAG, e.toString());
}
}
/**
* Validates sensors, makes sure that bluetooth is on and each sensor has a parameter associated with it
*/
void validateBioSensors() {
// First make sure that bluetooth is enabled
if (!mBluetoothEnabled) {
AlertDialog.Builder alert1 = new AlertDialog.Builder(this);
alert1.setMessage("Bluetooth is not enabled on your device. Press OK to go to the wireless system" +
"settings to turn it on");
alert1.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
mInstance.startActivityForResult(new Intent(android.provider.Settings.ACTION_BLUETOOTH_SETTINGS), BLUETOOTH_SETTINGS_ID);
}
});
alert1.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
}
});
alert1.show();
}
String badSensorName = null;
// Now make sure that every device has a parameter associated with it
for (BioSensor sensor: mBioSensors) {
if (sensor.mEnabled) {
String param = SharedPref.getParamForDevice(mInstance, sensor.mBTAddress);
//Log.d(TAG, "sensor: " + sensor.mBTName + ", parameter: " + param);
if (param == null) {
badSensorName = sensor.mBTName;
break;
}
}
} // end for (BioSensor sensor: mBioSensors)
if (badSensorName != null) {
AlertDialog.Builder alert1 = new AlertDialog.Builder(this);
alert1.setMessage("Sensor " + badSensorName + " is enabled but " +
" does not have a parameter associated with it." +
"Press Ok to associate one");
alert1.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
Intent intent2 = new Intent(mInstance, DeviceManagerActivity.class);
mInstance.startActivity(intent2);
}
});
alert1.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
}
});
alert1.show();
}
}
/**
* Wrapper for BioParameter that has a graph element
*
* @author scott.coleman
*
*/
static class GraphBioParameter extends BioParameter{
public XYSeries series;
public Boolean isShimmer;
Node shimmerNode;
byte shimmerSensorConstant;
public GraphBioParameter(long id, String title1, String title2, Boolean enabled) {
super(id, title1, title2, enabled);
isShimmer = false;
shimmerNode = null;
shimmerSensorConstant = 0;
series = new XYSeries(title1);
}
}
@Override
public void errorCallback() {
// TODO Auto-generated method stub
}
@Override
public void notifyAntStateChanged() {
// TODO Auto-generated method stub
}
@Override
public void notifyChannelStateChanged(byte channel) {
// TODO Auto-generated method stub
}
@Override
public void notifyChannelDataChanged(byte channel) {
HeartBeatData thisData = new HeartBeatData();
thisData.setFunctionCode(SPINEFunctionConstants.HEARTBEAT);
thisData.setBPM(mAntManager.getBPM());
this.received(thisData);
}
private final ServiceConnection mConnection = new ServiceConnection()
{
@Override
public void onServiceDisconnected(ComponentName name)
{
//This is very unlikely to happen with a local service (ie. one in the same process)
mAntManager.setCallbacks(null);
mAntManager = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service)
{
mAntManager = ((ANTPlusService.LocalBinder)service).getManager();
mAntManager.setCallbacks(MeditationActivity.this);
loadAntState();
notifyAntStateChanged();
// Start ANT automatically
mAntManager.doEnable();
Log.i(TAG, "Starting heart rate data");
mAntManager.openChannel(AntPlusManager.HRM_CHANNEL, true);
mAntManager.requestReset();
}
};
/**
* Store application persistent data.
*/
private void saveAntState()
{
// Save current Channel Id in preferences
// We need an Editor object to make changes
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putInt("DeviceNumberHRM", mAntManager.getDeviceNumberHRM());
editor.putInt("DeviceNumberSDM", mAntManager.getDeviceNumberSDM());
editor.putInt("DeviceNumberWGT", mAntManager.getDeviceNumberWGT());
editor.putInt("ProximityThreshold", mAntManager.getProximityThreshold());
editor.putInt("BufferThreshold", mAntManager.getBufferThreshold());
editor.commit();
}
/**
* Retrieve application persistent data.
*/
private void loadAntState()
{
// Restore preferences
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
mAntManager.setDeviceNumberHRM((short) settings.getInt("DeviceNumberHRM", ANT_WILDCARD));
mAntManager.setDeviceNumberSDM((short) settings.getInt("DeviceNumberSDM", ANT_WILDCARD));
mAntManager.setDeviceNumberWGT((short) settings.getInt("DeviceNumberWGT", ANT_WILDCARD));
mAntManager.setProximityThreshold((byte) settings.getInt("ProximityThreshold", ANT_DEFAULT_BIN));
mAntManager.setBufferThreshold((short) settings.getInt("BufferThreshold", ANT_DEFAULT_BUFFER_THRESHOLD));
}
private long map(long x, long in_min, long in_max, long out_min, long out_max)
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
private double map(double x, double in_min, double in_max, double out_min, double out_max)
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
}