package hevs.aislab.magpie.watch; import android.content.BroadcastReceiver; import android.content.Context; import android.content.IntentFilter; import android.hardware.SensorManager; import android.support.v4.app.FragmentManager; import android.content.Intent; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.os.Bundle; import android.speech.RecognizerIntent; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import android.view.View; import java.util.ArrayList; import java.util.List; import ch.hevs.aislab.magpie.agent.MagpieAgent; import ch.hevs.aislab.magpie.android.MagpieActivityWatch; import ch.hevs.aislab.magpie.behavior.PriorityBehaviorAgentMind; import ch.hevs.aislab.magpie.environment.Services; import ch.hevs.aislab.magpie.event.LogicTupleEvent; import hevs.aislab.magpie.watch.agents.GlucoseBehaviour; import hevs.aislab.magpie.watch.agents.PressureBehaviour; import hevs.aislab.magpie.watch.agents.PulseBehaviour; import hevs.aislab.magpie.watch.agents.StepBehaviour; import hevs.aislab.magpie.watch.agents.WeightBehaviour; import hevs.aislab.magpie.watch.gui.fragment.FragmentDisplayAlertes; import hevs.aislab.magpie.watch.gui.dialogfragment.DialogFragmentSetPressure; import hevs.aislab.magpie.watch.gui.dialogfragment.DialogFragmentSetGlucose; import hevs.aislab.magpie.watch.gui.fragment.FragmentHome; import hevs.aislab.magpie.watch.gui.fragment.FragmentSettings; import hevs.aislab.magpie.watch.gui.dialogfragment.DialogFragmentSetWeight; import hevs.aislab.magpie.watch.models.CustomRules; import hevs.aislab.magpie.watch.notification.CustomToast; import hevs.aislab.magpie.watch.shared_pref.PrefAccessor; import hevs.aislab.magpie.watch.threads.IhomeActivity; import hevs.aislab.magpie.watch.threads.SensorsThreadLifecircle; import hevs.aislab.magpie.watch_library.lib.Const; import hevs.aislab.magpie.watch_library.lib.DateFormater; /** * * this class will handle all other fragment related the the apps and contains the MAGPIE logic */ //implement the listener for the sensors public class HomeActivity extends MagpieActivityWatch implements SensorEventListener,IdialogToActivity, IhomeActivity { //handle the voice variable private static final int SPEECH_REQUEST_CODE = 0; //fragment private FragmentHome fragmentHome; private FragmentSettings fragmentSettings; private FragmentDisplayAlertes fragmentDisplayAlertes; //sensors private SensorManager sensorManager; private Sensor sensor_pulse; private Sensor sensor_step; private Thread threadPulse; //used to know the accuracy of the pulse sensors private int accuracySensorPulse; //List where we will store value in the pulse to make the average private ArrayList<Double>listPulse=new ArrayList<Double>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_home); initFragment(); //display the fragment home witout any value //init the sensors sensorManager=(SensorManager) getSystemService(SENSOR_SERVICE); sensor_pulse=sensorManager.getDefaultSensor(Sensor.TYPE_HEART_RATE); sensor_step=sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR); //register the sensors sensorManager.registerListener(this,sensor_step,SensorManager.SENSOR_DELAY_NORMAL); //activate a thread for the pulse sensors //process information: 30 sec //stop time= 30 min threadPulse=new SensorsThreadLifecircle(this,sensor_pulse,30000,1000*60*30); threadPulse.start(); displayFragmentHome(); // Register the local broadcast receiver. make the ling between the Service and the view IntentFilter messageFilter = new IntentFilter(Intent.ACTION_SEND); MessageReceiver messageReceiver = new MessageReceiver(); LocalBroadcastManager.getInstance(this).registerReceiver(messageReceiver, messageFilter); } /** * USED TO INIT ALL FRAGMENT */ private void initFragment() { fragmentSettings=new FragmentSettings(); fragmentHome=new FragmentHome(); fragmentDisplayAlertes=new FragmentDisplayAlertes(); getSupportFragmentManager().beginTransaction().add(R.id.main_container,fragmentSettings,"settingsFrag").commit(); getSupportFragmentManager().beginTransaction().add(R.id.main_container,fragmentHome,"homeFrag").commit(); getSupportFragmentManager().beginTransaction().add(R.id.main_container,fragmentDisplayAlertes,"display_alertTag").commit(); } // BUTTON MENU EVENT public void click_voiceRecongnition(View view) { displaySpeechRecognizer(); } public void click_home(View view ) { displayFragmentHome(); } public void click_settings(View view ) { displayFragmentSettings(); } //UPDATE VIEW IN FRAGMENT public void click_alert(View view) { displayFragmentAlert(); } //------------------METHODE TO PROCESS VALUE FROM THE DIALOG FRAGMENT--------------------- /** * * Used by the dialog fragment to communicate with the activity. * @param category * @param value */ @Override public void sendValue(String category, String ... value) { //check the entry to see is valide. if not, we don't send value to magpie //send to magpie if it's valide processEvent(System.currentTimeMillis(),category,value); } /** * USED TO UPDATE THE BAR AREA OF EACH CATEGORY. it will delegate the task to the fragment * @param rules */ @Override public void updateBarArea(CustomRules rules) { //if the category is pressure, we will have to update the systol and the diastol progress bar if (rules.getCategory().equals(Const.CATEGORY_PRESSURE)) { fragmentHome.ajustBarLevel(rules,Const.CATEGORY_SYSTOL); fragmentHome.ajustBarLevel(rules,Const.CATEGORY_DIASTOL); return; } fragmentHome.ajustBarLevel(rules,rules.getCategory()); } //-------------------DIALOG FRAGMENT -------------------- /** * * Open the dialog fragment glucose. Then, user will be able to add new glucose value. * @param view */ public void click_setGlucose(View view) { FragmentManager fm = getSupportFragmentManager(); DialogFragmentSetGlucose myDialogFragment = new DialogFragmentSetGlucose(); myDialogFragment.show(fm,"tag"); } public void click_setWeight(View view) { FragmentManager fm = getSupportFragmentManager(); DialogFragmentSetWeight myDialogFragment = new DialogFragmentSetWeight(); myDialogFragment.show(fm,"tag"); } public void click_setPressure(View view) { FragmentManager fm = getSupportFragmentManager(); DialogFragmentSetPressure myDialogFragment = new DialogFragmentSetPressure(); myDialogFragment.show(fm,"tag"); } //only inform the user that the measure are taken automaticaly public void click_setPulse(View view) { CustomToast.getInstance().warningToast(getString(R.string.information_pulse_device),this); //TODO TESTE IMPLEMENTATION ! REMOVE THIS CODE BELOW IN PROD ! ONLY FOR TEST. processEvent(System.currentTimeMillis(),Const.CATEGORY_PULSE,"100"); } public void click_setSteps(View view) { CustomToast.getInstance().warningToast(getString(R.string.information_step_device),this); //only used for test. It will add 1000 steps. IN PRODUCTION, REMOVE THIS! addStep(1000); } //------------VOICE RECOGNITION HANDLER------------------ /** * Create the voice activty. It will call voice activity and capture voice */ private void displaySpeechRecognizer() { Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); // Start the activity, the intent will be populated with the speech text startActivityForResult(intent, SPEECH_REQUEST_CODE); } //handle the voice result @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == SPEECH_REQUEST_CODE && resultCode == RESULT_OK) { handleVoiceEvent(data); } super.onActivityResult(requestCode, resultCode, data); } /** * DEFINE THE ACTION IN RESPONSE OF VOICE EVENT. Used to handle voice event (when they return back from the voice activity) */ private void handleVoiceEvent(Intent data) { List<String> results = data.getStringArrayListExtra( RecognizerIntent.EXTRA_RESULTS); //get the spocken language in a string String spokenText = results.get(0).toLowerCase(); //replace eventual comma by "." spokenText = spokenText.replace(",", "."); String glucose = getString(R.string.voice_glucose); //split the texte spoken into an array. The first value will be category, the second the value String[] arraySpocken = spokenText.split(" "); //ad the glucose by voice if (arraySpocken[0].toLowerCase().equals(glucose)) { if (arraySpocken.length >= 1) { voiceAction_addValue(Const.CATEGORY_GLUCOSE, arraySpocken[1]); return; } // no number has been specified, so command is not completed CustomToast.getInstance().warningToast(getString(R.string.voice_incomplet_number), this); return; } String pressure=getString(R.string.voice_pressure); if (arraySpocken[0].toLowerCase().equals(pressure)) { if (arraySpocken.length >= 3) { voiceAction_addValue( Const.CATEGORY_PRESSURE, arraySpocken[1],arraySpocken[3]); return; } // no number has been specified, so command is not complet CustomToast.getInstance().errorTOast(getString(R.string.voice_incomplet_number), this); return; } String weight=getString(R.string.voice_weight); if (arraySpocken[0].toLowerCase().equals(weight)) { if (arraySpocken.length>=2) { voiceAction_addValue(Const.CATEGORY_WEIGHT,arraySpocken[1]); return; } CustomToast.getInstance().errorTOast(getString(R.string.voice_incorrect), this); } CustomToast.getInstance().errorTOast(getString(R.string.voice_not_found), this); } /** * ADD A VALUE FROM THE VOICE ACTION * @param category The category we want to add value * @param rawvalue THe array of value. String and we can set it to the value we want */ private void voiceAction_addValue( String category, String ... rawvalue) { //we set the value of the glucose try { //get the number. handle eventual null value or not number value, or the fact that the framgent is not fully charged double value[]=new double[rawvalue.length]; //try to cast all value into double. if it fails, we return passe in the exception for (int k=0;k<value.length;k++) { value[k]=Double.parseDouble(rawvalue[k]); } processEvent(System.currentTimeMillis(), category,rawvalue); } catch (NumberFormatException ex) { ex.printStackTrace(); CustomToast.getInstance().errorTOast(getString(R.string.voice_incorrect), this); } catch (NullPointerException ex) { ex.printStackTrace(); CustomToast.getInstance().errorTOast("unespected error", this); } } private void displayFragmentHome() { getSupportFragmentManager().beginTransaction().hide(fragmentSettings).commit(); getSupportFragmentManager().beginTransaction().hide(fragmentDisplayAlertes).commit(); getSupportFragmentManager().beginTransaction().show(fragmentHome).commit(); } private void displayFragmentSettings() { getSupportFragmentManager().beginTransaction().hide(fragmentDisplayAlertes).commit(); getSupportFragmentManager().beginTransaction().hide(fragmentHome).commit(); getSupportFragmentManager().beginTransaction().show(fragmentSettings).commit(); // newInstance() is a static factory method. } private void displayFragmentAlert() { getSupportFragmentManager().beginTransaction().hide(fragmentSettings).commit(); getSupportFragmentManager().beginTransaction().hide(fragmentHome).commit(); getSupportFragmentManager().beginTransaction().remove(fragmentDisplayAlertes).commit(); //create a new fragment each time to be sure to have the last values fragmentDisplayAlertes=new FragmentDisplayAlertes(); getSupportFragmentManager().beginTransaction().add(R.id.main_container,fragmentDisplayAlertes,"display_alertTag").commit(); getSupportFragmentManager().beginTransaction().show(fragmentDisplayAlertes).commit(); } //-----------MAGPIE METHODE------------- @Override public void onEnvironmentConnected() { //Java rules MagpieAgent behaviorAgent = new MagpieAgent("priority_agent", Services.LOGIC_TUPLE); PriorityBehaviorAgentMind behaviorMind = new PriorityBehaviorAgentMind(); behaviorMind.addBehavior(new PulseBehaviour(this, behaviorAgent, 1)); behaviorMind.addBehavior(new GlucoseBehaviour(this,behaviorAgent,1)); behaviorMind.addBehavior(new PressureBehaviour(this,behaviorAgent,1)); behaviorMind.addBehavior(new StepBehaviour(this,behaviorAgent,1)); behaviorMind.addBehavior(new WeightBehaviour(this,behaviorAgent,1)); behaviorAgent.setMind(behaviorMind); registerAgent(behaviorAgent); } @Override public void onAlertProduced(LogicTupleEvent alert) { } //PROCESS EVENT WITH MAGPIE /** * Send the event to magpie agent * @param timeStamp Timestamp of the event * @param category Category of the event * @param value The values of the events */ public void processEvent(long timeStamp, String category, String ... value ) { Log.d("sendToPRocess","send to process"); LogicTupleEvent lte = new LogicTupleEvent(category, value); lte.setTimestamp(timeStamp); sendEvent(lte); } //this methode will be called by the thread. we will send the pulse to magpie (average of pulse during a certain periode of time) /** * Used to process pulse. This method is called directly from the thread */ @Override public void processPulse() { if (listPulse.size()==0) return; double average=0; //make the average of the list for(Double aPulse : listPulse) { average+=aPulse; } average/=listPulse.size(); Log.d("averageOfPulse:",average+""); processEvent(System.currentTimeMillis(),Const.CATEGORY_PULSE,average+""); listPulse.clear(); } //-------------SENSORS METHODE--------------------------- /** * Used to handle all sensors event (in our case, is step and heart pulse) * @param sensorEvent */ @Override public void onSensorChanged(SensorEvent sensorEvent) { switch (sensorEvent.sensor.getType()) { //handle heart event case Sensor.TYPE_HEART_RATE : //add a value to the array, but only if the accuracy is at least at 1 (-1 == no contact, 3 == best contact) if (accuracySensorPulse<1) return; double value=sensorEvent.values[0]; listPulse.add(value); break; case Sensor.TYPE_STEP_DETECTOR : //for this sensor, we juste update the value. the value is processed only once a day addStep(1); break; } } /** * Used to handle change of accuracy from the sensors. We will only check pulse accuracy * @param sensor * @param i */ @Override public void onAccuracyChanged(Sensor sensor, int i) { //set the accuracy of the pulse snsors if (sensor.getType()==Sensor.TYPE_HEART_RATE) { accuracySensorPulse=i; } } /** * Used when the user to a step * @param newStep */ private void addStep(long newStep) { //retrive the current step long currentStep= PrefAccessor.getInstance().getLong(this, Const.KEY_CURRENT_STEP); //check if it's a new day. If it is, we store the actual data in the data base String today= DateFormater.getInstance().getDate(System.currentTimeMillis()); if (!today.equals(PrefAccessor.getInstance().getString(this,Const.KEY_DATE_STEP))) { //write the new date in shared pref PrefAccessor.getInstance().save(this,Const.KEY_DATE_STEP,today); //send event to magpie processEvent(System.currentTimeMillis(),Const.CATEGORY_STEP,currentStep+""); //reset the number of step currentStep=0; } //get the current step. 0 if null. currentStep+=newStep; PrefAccessor.getInstance().save(this,Const.KEY_CURRENT_STEP,currentStep); //change the display of the step fragmentHome.setStepValue((double)currentStep); } //***************************************HANDLE THE LIFE CIRCLE OF SENSORS*************************** /** * * @param sensor THe sensor we want to activate * @param sensorsType THe sensors type we want */ @Override public void registerSensor(Sensor sensor, int sensorsType) { sensorManager.registerListener(this,sensor,sensorsType); } /** * Used to disactivate sensors * @param sensor */ @Override public void unregisterSensors(Sensor sensor) { sensorManager.unregisterListener(this,sensor); } public FragmentHome getFragmentHome() { return this.fragmentHome; } @Override public void onBackPressed() { finish(); super.onBackPressed(); } /** * used to destroy the current thread running (thread that manage pulse) */ @Override protected void onDestroy() { Log.d("DestroyHasBennCalled","blablalbalblablablablabl"); //close the current thread for the pulse //interrupt and stop the current running thread ((SensorsThreadLifecircle)threadPulse).cancel(); threadPulse.interrupt(); super.onDestroy(); } // @Override // protected void onSaveInstanceState(Bundle outState) { // //No call for super(). Bug on API Level > 11. // } //handle data receive from the service /** * This class is used to handle data from the different services (LIstener). It will make the link between the service and the * activity */ private class MessageReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Bundle data = intent.getBundleExtra(Const.KEY_BROADCASTdATA); CustomRules rule=getRuleFromMap(data); //make change in the progress bar updateBarArea(rule); //create a Toast to inform that we have new rule toastConfirmDataReceive(rule); } } /** * Create a toast to confirme data have been received * @param rule */ public void toastConfirmDataReceive(CustomRules rule) { CustomToast.getInstance().confirmToast("Rule "+rule.getCategory()+" updated",this); } /** * Parse a dataMap into a rule object * @param dataMap * @return */ private CustomRules getRuleFromMap(Bundle dataMap) { CustomRules rule =new CustomRules(); rule.setId(dataMap.getLong(Const.KEY_RULE_ID)); rule.setCategory(dataMap.getString(Const.KEY_RULE_CATEGORY)); rule.setConstraint_1(dataMap.getString(Const.KEY_RULE_CONSTRAINT1)); rule.setConstraint_2(dataMap.getString(Const.KEY_RULE_CONSTRAINT2)); rule.setConstraint_3(dataMap.getString(Const.KEY_RULE_CONSTRAINT3)); rule.setVal_1_min(formatValue( dataMap.getDouble(Const.KEY_RULE_VAL1_MIN))); rule.setVal_1_max(formatValue(dataMap.getDouble(Const.KEY_RULE_VAL1_MAX))); rule.setVal_2_min(formatValue(dataMap.getDouble(Const.KEY_RULE_VAL2_MIN))); rule.setVal_2_max(formatValue(dataMap.getDouble(Const.KEY_RULE_VAL2_MAX))); return rule; } public Double formatValue(Double value) { return value==Const.NULL_IDENTIFIER ? null : value; } }