diff --git a/README.md b/README.md
index d65e94b18..a0639ee19 100644
--- a/README.md
+++ b/README.md
@@ -119,7 +119,6 @@ This repository holds the Android App for performing experiments with [PSLab](ht
## How to set up the Android app in your development environment
-
### Application Flavors
There are 2 flavors (build variants) of PSLab Android application.
@@ -218,17 +217,9 @@ Despite any reason, follow the steps given below to squash all commits into one
### Branch Policy
We have the following branches
- * **development**
- All development goes on in this branch. If you're making a contribution,
- you are supposed to make a pull request to _development_.
- Make sure it passes a build check on Travis.
-
- * **master**
- This contains the stable code. After significant features/bugfixes are accumulated on development, we move it to master.
-
- * **apk**
- This branch contains automatically generated apk file for testing.
-
+* **development** All development goes on in this branch. If you're making a contribution, you are supposed to make a pull request to _development_. Make sure it passes a build check on Travis.
+* **master** This contains the stable code. After significant features/bugfixes are accumulated on development, we move it to master.
+* **apk** This branch contains automatically generated apk file for testing.
### Code style
@@ -239,11 +230,9 @@ Please try to follow the mentioned guidelines while writing and submitting your
* The activity/fragment file name corresponding to the layout files should be named as (activity/fragment name)(activity/fragment).java like ```ChannelsParameterFragment.java``` corresponding to the layout file ```fragment_channels_parameter.xml``` .
* The corresponding widgets for buttons, textboxes, checkboxes etc. in activity files should be named as (viewtype/widget)(fragment/activity name)(no. in the file) like ```spinnerChannelSelect1``` corresponding to ```spinner_channel_select1``` .
-## License
-
-This project is currently licensed under the Apache License 2.0. A copy of [LICENSE](LICENSE) is to be present along with the source code. To obtain the software under a different license, please contact FOSSASIA.
+## Developers
-## Maintainers
+### Maintainers
The project is maintained by
- Padmal ([@CloudyPadmal](https://github.com/CloudyPadmal))
- Mario Behling ([@mariobehling](http://github.com/mariobehling))
@@ -251,9 +240,8 @@ The project is maintained by
- Wei Tat ([@cweitat](https://github.com/cweitat))
- Wai Gie ([@woshikie](https://github.com/woshikie))
- Neel Trivedi ([@neel1998](https://github.com/neel1998))
-## Alumni
-- Praveen Patil ([@wavicles](https://github.com/wavicles))
-- Jithin ([@jithinbp](https://github.com/jithinbp))
+
+### Alumni
- Akarshan Gandotra ([@akarshan96](https://github.com/akarshan96))
- Asitava Sarkar ([@asitava1998](https://github.com/asitava1998))
- Vivek Singh Bhadauria ([@viveksb007](https://github.com/viveksb007))
@@ -261,3 +249,7 @@ The project is maintained by
- Abhinav ([@abhinavraj23](https://github.com/abhinavraj23))
- Harsh ([@harsh-2711](https://github.com/harsh-2711))
- Yatri ([@yatri1609](https://github.com/yatri1609))
+
+## License
+
+This project is currently licensed under the Apache License 2.0. A copy of [LICENSE](LICENSE) is to be present along with the source code. To obtain the software under a different license, please contact FOSSASIA.
diff --git a/app/build.gradle b/app/build.gradle
index 4b09aefbe..b16b01540 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -13,8 +13,8 @@ android {
applicationId "io.pslab"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
- versionCode 20
- versionName "2.0.19"
+ versionCode 21
+ versionName "2.0.20"
multiDexEnabled true
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 00321a931..93e4c8bac 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -103,6 +103,9 @@
android:name=".activity.GasSensorActivity"
android:screenOrientation="portrait" />
+
diff --git a/app/src/main/java/io/pslab/CheckBoxGetter.java b/app/src/main/java/io/pslab/CheckBoxGetter.java
new file mode 100644
index 000000000..d79dec077
--- /dev/null
+++ b/app/src/main/java/io/pslab/CheckBoxGetter.java
@@ -0,0 +1,30 @@
+package io.pslab;
+
+import java.io.Serializable;
+
+public class CheckBoxGetter implements Serializable {
+
+ private String name;
+ private boolean isSelected;
+
+ public CheckBoxGetter(String name, boolean isSelected) {
+ this.name = name;
+ this.isSelected = isSelected;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public boolean isSelected() {
+ return isSelected;
+ }
+
+ public void setSelected(boolean selected) {
+ isSelected = selected;
+ }
+}
diff --git a/app/src/main/java/io/pslab/activity/CreateConfigActivity.java b/app/src/main/java/io/pslab/activity/CreateConfigActivity.java
index 5e2ec1d37..3d9a4501b 100644
--- a/app/src/main/java/io/pslab/activity/CreateConfigActivity.java
+++ b/app/src/main/java/io/pslab/activity/CreateConfigActivity.java
@@ -4,16 +4,15 @@
import android.os.Environment;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
-import android.view.Gravity;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
-import android.widget.CheckBox;
import android.widget.EditText;
-import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.Toast;
@@ -21,8 +20,11 @@
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.List;
+import io.pslab.CheckBoxGetter;
import io.pslab.R;
+import io.pslab.adapters.CheckBoxAdapter;
import io.pslab.others.CSVLogger;
import io.pslab.others.CustomSnackBar;
@@ -36,7 +38,8 @@ public class CreateConfigActivity extends AppCompatActivity {
private EditText intervalEditText;
private String interval;
private View rootView;
- private LinearLayout paramsListContainer;
+ private RecyclerView paramsListContainer;
+ private List list;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -58,6 +61,8 @@ protected void onCreate(Bundle savedInstanceState) {
instrumentsList = new ArrayList<>();
instrumentParamsList = new ArrayList<>();
instrumentParamsListTitles = new ArrayList<>();
+ paramsListContainer.setLayoutManager(new LinearLayoutManager(this));
+ list = new ArrayList<>();
createArrayLists();
selectInstrumentSpinner.setAdapter(new ArrayAdapter(this, android.R.layout.simple_list_item_1, instrumentsList));
selectInstrumentSpinner.setSelection(0, true);
@@ -97,9 +102,9 @@ public void onClick(View v) {
Toast.makeText(CreateConfigActivity.this, getResources().getString(R.string.no_interval_message), Toast.LENGTH_SHORT).show();
} else {
ArrayList selectedParamsList = new ArrayList<>();
- for (int i = 0; i < paramsListContainer.getChildCount(); i ++) {
- CheckBox checkBox = (CheckBox) paramsListContainer.getChildAt(i);
- if (checkBox.isChecked()) {
+ for (int i = 0; i < paramsListContainer.getChildCount(); i++) {
+ boolean checkBox = list.get(i).isSelected();
+ if (checkBox) {
selectedParamsList.add(instrumentParamsList.get(selectedItem)[i]);
}
}
@@ -134,17 +139,15 @@ private void createArrayLists() {
}
private void createCheckboxList() {
- paramsListContainer.removeAllViews();
+ list.clear();
String[] params = instrumentParamsListTitles.get(selectedItem);
- for (int i = 0; i < params.length; i++){
- CheckBox checkBox = new CheckBox(CreateConfigActivity.this);
- checkBox.setText(params[i]);
- LinearLayout.LayoutParams checkBoxParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT);
- checkBoxParams.gravity = Gravity.CENTER_HORIZONTAL;
- checkBoxParams.setMargins(0,(int)getResources().getDimension(R.dimen.create_config_margin1),0,0);
- checkBox.setLayoutParams(checkBoxParams);
- paramsListContainer.addView(checkBox, i);
+ for (int i = 0; i < params.length; i++) {
+ CheckBoxGetter check = new CheckBoxGetter(params[i], false);
+ list.add(check);
}
+ CheckBoxAdapter box;
+ box = new CheckBoxAdapter(CreateConfigActivity.this, list);
+ paramsListContainer.setAdapter(box);
}
@Override
diff --git a/app/src/main/java/io/pslab/activity/DustSensorActivity.java b/app/src/main/java/io/pslab/activity/DustSensorActivity.java
new file mode 100644
index 000000000..09c0c6e3a
--- /dev/null
+++ b/app/src/main/java/io/pslab/activity/DustSensorActivity.java
@@ -0,0 +1,135 @@
+package io.pslab.activity;
+
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+import android.support.v4.app.Fragment;
+
+import io.pslab.R;
+import io.pslab.fragment.DustSensorDataFragment;
+import io.pslab.fragment.DustSensorSettingsFragment;
+import io.pslab.models.DustSensorData;
+import io.pslab.models.PSLabSensor;
+import io.pslab.models.SensorDataBlock;
+import io.pslab.others.LocalDataLog;
+import io.realm.RealmObject;
+import io.realm.RealmResults;
+
+public class DustSensorActivity extends PSLabSensor {
+
+ private static final String PREF_NAME = "customDialogPreference";
+ public RealmResults recordedDustSensorData;
+
+ @Override
+ public int getMenu() {
+ return R.menu.sensor_data_log_menu;
+ }
+
+ @Override
+ public SharedPreferences getStateSettings() {
+ return this.getSharedPreferences(PREF_NAME, MODE_PRIVATE);
+ }
+
+ @Override
+ public String getFirstTimeSettingID() {
+ return "DustSensorFirstTime";
+ }
+
+ @Override
+ public String getSensorName() {
+ return getResources().getString(R.string.dust_sensor);
+ }
+
+ @Override
+ public int getGuideTitle() {
+ return R.string.dust_sensor;
+ }
+
+ @Override
+ public int getGuideAbstract() {
+ return R.string.dust_sensor_intro;
+ }
+
+ @Override
+ public int getGuideSchematics() {
+ return 0;
+ }
+
+ @Override
+ public int getGuideDescription() {
+ return R.string.dust_sensor_desc;
+ }
+
+ @Override
+ public int getGuideExtraContent() {
+ return 0;
+ }
+
+ @Override
+ public void recordSensorDataBlockID(SensorDataBlock block) {
+ realm.beginTransaction();
+ realm.copyToRealm(block);
+ realm.commitTransaction();
+ }
+
+ @Override
+ public void recordSensorData(RealmObject sensorData) {
+ realm.beginTransaction();
+ realm.copyToRealm((DustSensorData) sensorData);
+ realm.commitTransaction();
+ }
+
+ @Override
+ public void stopRecordSensorData() {
+ LocalDataLog.with().refresh();
+ }
+
+ @Override
+ public Fragment getSensorFragment() {
+ return DustSensorDataFragment.newInstance();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ reinstateConfigurations();
+ }
+
+ private void reinstateConfigurations() {
+ SharedPreferences luxMeterConfigurations;
+ luxMeterConfigurations = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
+ locationEnabled = luxMeterConfigurations.getBoolean(DustSensorSettingsFragment.KEY_INCLUDE_LOCATION, true);
+ DustSensorDataFragment.setParameters(
+ getValueFromText(luxMeterConfigurations.getString(DustSensorSettingsFragment.KEY_HIGH_LIMIT, "4.0"),
+ 0.0, 5.0),
+ getValueFromText(luxMeterConfigurations.getString(DustSensorSettingsFragment.KEY_UPDATE_PERIOD, "1000"),
+ 100, 1000),
+ luxMeterConfigurations.getString(DustSensorSettingsFragment.KEY_DUST_SENSOR_TYPE, "0"));
+ }
+
+ private int getValueFromText(String strValue, int lowerBound, int upperBound) {
+ if (strValue.isEmpty()) return lowerBound;
+ int value = Integer.parseInt(strValue);
+ if (value > upperBound) return upperBound;
+ else if (value < lowerBound) return lowerBound;
+ else return value;
+ }
+
+ private double getValueFromText(String strValue, double lowerBound, double upperBound) {
+ if (strValue.isEmpty()) return lowerBound;
+ double value = Double.parseDouble(strValue);
+ if (value > upperBound) return upperBound;
+ else if (value < lowerBound) return lowerBound;
+ else return value;
+ }
+
+ @Override
+ public void getDataFromDataLogger() {
+ if (getIntent().getExtras() != null && getIntent().getExtras().getBoolean(KEY_LOG)) {
+ viewingData = true;
+ recordedDustSensorData = LocalDataLog.with()
+ .getBlockOfDustSensorRecords(getIntent().getExtras().getLong(DATA_BLOCK));
+ String title = titleFormat.format(recordedDustSensorData.get(0).getTime());
+ getSupportActionBar().setTitle(title);
+ }
+ }
+}
diff --git a/app/src/main/java/io/pslab/activity/RoboticArmActivity.java b/app/src/main/java/io/pslab/activity/RoboticArmActivity.java
index 1bb06e6cb..d86769b27 100644
--- a/app/src/main/java/io/pslab/activity/RoboticArmActivity.java
+++ b/app/src/main/java/io/pslab/activity/RoboticArmActivity.java
@@ -150,6 +150,11 @@ public void onClick(View v) {
scrollView = findViewById(R.id.horizontal_scroll_view);
servoCSVLogger = new CSVLogger(getResources().getString(R.string.robotic_arm));
+ degreeText1.setText(getResources().getString(R.string.zero));
+ degreeText2.setText(getResources().getString(R.string.zero));
+ degreeText3.setText(getResources().getString(R.string.zero));
+ degreeText4.setText(getResources().getString(R.string.zero));
+
LinearLayout.LayoutParams servoControllerParams = new LinearLayout.LayoutParams(screen_width / 4 - 4, screen_height / 2 - 4);
servoControllerParams.setMargins(2, 5, 2, 0);
servo1Layout.setLayoutParams(servoControllerParams);
@@ -173,7 +178,7 @@ public void onClick(View v) {
timeLineBox.setLayoutParams(servoTimeLineBoxParams);
timeLineBox.setPadding(5, 5, 5, 5);
TextView timeText = timeLineBox.findViewById(R.id.timeline_box_time_text);
- timeText.setText(String.valueOf(i + 1));
+ timeText.setText(String.valueOf(i + 1) + getResources().getString(R.string.robotic_arm_second_unit));
timeLineBox.setOnDragListener(servo1DragListener);
servo1TimeLine.addView(timeLineBox, i);
}
@@ -183,7 +188,7 @@ public void onClick(View v) {
timeLineBox.setLayoutParams(servoTimeLineBoxParams);
timeLineBox.setPadding(5, 5, 5, 5);
TextView timeText = timeLineBox.findViewById(R.id.timeline_box_time_text);
- timeText.setText(String.valueOf(i + 1));
+ timeText.setText(String.valueOf(i + 1) + getResources().getString(R.string.robotic_arm_second_unit));
timeLineBox.setOnDragListener(servo2DragListener);
servo2TimeLine.addView(timeLineBox, i);
}
@@ -193,7 +198,7 @@ public void onClick(View v) {
timeLineBox.setLayoutParams(servoTimeLineBoxParams);
timeLineBox.setPadding(5, 5, 5, 5);
TextView timeText = timeLineBox.findViewById(R.id.timeline_box_time_text);
- timeText.setText(String.valueOf(i + 1));
+ timeText.setText(String.valueOf(i + 1) + getResources().getString(R.string.robotic_arm_second_unit));
timeLineBox.setOnDragListener(servo3DragListener);
servo3TimeLine.addView(timeLineBox, i);
}
@@ -203,7 +208,7 @@ public void onClick(View v) {
timeLineBox.setLayoutParams(servoTimeLineBoxParams);
timeLineBox.setPadding(5, 5, 5, 5);
TextView timeText = timeLineBox.findViewById(R.id.timeline_box_time_text);
- timeText.setText(String.valueOf(i + 1));
+ timeText.setText(String.valueOf(i + 1) + getResources().getString(R.string.robotic_arm_second_unit));
timeLineBox.setOnDragListener(servo4DragListener);
servo4TimeLine.addView(timeLineBox, i);
}
@@ -467,13 +472,13 @@ public void onTick(long millisUntilFinished) {
timeIndicatorLayout.setLayoutParams(timeIndicatorParams);
scrollView.smoothScrollBy(screen_width / 6, 0);
String deg1 = ((TextView) servo1TimeLine.getChildAt(timelinePosition).findViewById(R.id.timeline_box_degree_text)).getText().toString();
- deg1 = (deg1.length() > 0) ? deg1 : "0";
+ deg1 = (deg1.length() > 0) ? deg1.substring(0, deg1.length() - 1) : getResources().getString(R.string.zero);
String deg2 = ((TextView) servo2TimeLine.getChildAt(timelinePosition).findViewById(R.id.timeline_box_degree_text)).getText().toString();
- deg2 = (deg2.length() > 0) ? deg2 : "0";
+ deg2 = (deg2.length() > 0) ? deg2.substring(0, deg2.length() - 1) : getResources().getString(R.string.zero);
String deg3 = ((TextView) servo3TimeLine.getChildAt(timelinePosition).findViewById(R.id.timeline_box_degree_text)).getText().toString();
- deg3 = (deg3.length() > 0) ? deg3 : "0";
+ deg3 = (deg3.length() > 0) ? deg3.substring(0, deg3.length() - 1) : getResources().getString(R.string.zero);
String deg4 = ((TextView) servo4TimeLine.getChildAt(timelinePosition).findViewById(R.id.timeline_box_degree_text)).getText().toString();
- deg4 = (deg4.length() > 0) ? deg4 : "0";
+ deg4 = (deg4.length() > 0) ? deg4.substring(0, deg4.length() - 1) : getResources().getString(R.string.zero);
if (scienceLab.isConnected()) {
scienceLab.servo4(Double.valueOf(deg1), Double.valueOf(deg2), Double.valueOf(deg3), Double.valueOf(deg4));
}
@@ -567,10 +572,10 @@ private void setReceivedData() {
ArrayList servoDataList = new ArrayList(recordedServoData);
for (int i = 0; i < servoDataList.size(); i++) {
ServoData servoData = (ServoData) servoDataList.get(i);
- ((TextView) servo1TimeLine.getChildAt(i).findViewById(R.id.timeline_box_degree_text)).setText(servoData.getDegree1());
- ((TextView) servo2TimeLine.getChildAt(i).findViewById(R.id.timeline_box_degree_text)).setText(servoData.getDegree2());
- ((TextView) servo3TimeLine.getChildAt(i).findViewById(R.id.timeline_box_degree_text)).setText(servoData.getDegree3());
- ((TextView) servo4TimeLine.getChildAt(i).findViewById(R.id.timeline_box_degree_text)).setText(servoData.getDegree4());
+ ((TextView) servo1TimeLine.getChildAt(i).findViewById(R.id.timeline_box_degree_text)).setText(servoData.getDegree1() + getResources().getString(R.string.robotic_arm_degree_symbol));
+ ((TextView) servo2TimeLine.getChildAt(i).findViewById(R.id.timeline_box_degree_text)).setText(servoData.getDegree2() + getResources().getString(R.string.robotic_arm_degree_symbol));
+ ((TextView) servo3TimeLine.getChildAt(i).findViewById(R.id.timeline_box_degree_text)).setText(servoData.getDegree3() + getResources().getString(R.string.robotic_arm_degree_symbol));
+ ((TextView) servo4TimeLine.getChildAt(i).findViewById(R.id.timeline_box_degree_text)).setText(servoData.getDegree4() + getResources().getString(R.string.robotic_arm_degree_symbol));
}
}
@@ -585,18 +590,22 @@ private void saveTimeline() {
double lat, lon;
for (int i = 0; i < 60; i++) {
timestamp = System.currentTimeMillis();
- degree1 = degree2 = degree3 = degree4 = "0";
+ degree1 = degree2 = degree3 = degree4 = getResources().getString(R.string.zero);
if (((TextView) servo1TimeLine.getChildAt(i).findViewById(R.id.timeline_box_degree_text)).getText().length() > 0) {
degree1 = ((TextView) servo1TimeLine.getChildAt(i).findViewById(R.id.timeline_box_degree_text)).getText().toString();
+ degree1 = degree1.substring(0, degree1.length() - 1);
}
if (((TextView) servo2TimeLine.getChildAt(i).findViewById(R.id.timeline_box_degree_text)).getText().length() > 0) {
degree2 = ((TextView) servo2TimeLine.getChildAt(i).findViewById(R.id.timeline_box_degree_text)).getText().toString();
+ degree2 = degree2.substring(0, degree2.length() - 1);
}
if (((TextView) servo3TimeLine.getChildAt(i).findViewById(R.id.timeline_box_degree_text)).getText().length() > 0) {
degree3 = ((TextView) servo3TimeLine.getChildAt(i).findViewById(R.id.timeline_box_degree_text)).getText().toString();
+ degree3 = degree3.substring(0, degree3.length() - 1);
}
if (((TextView) servo4TimeLine.getChildAt(i).findViewById(R.id.timeline_box_degree_text)).getText().length() > 0) {
degree4 = ((TextView) servo4TimeLine.getChildAt(i).findViewById(R.id.timeline_box_degree_text)).getText().toString();
+ degree4 = degree4.substring(0, degree4.length() - 1);
}
if (gpsLogger.isGPSEnabled()) {
Location location = gpsLogger.getDeviceLocation();
@@ -638,7 +647,7 @@ public boolean onDrag(View v, DragEvent event) {
View view = (View) event.getLocalState();
TextView text = view.findViewById(R.id.degreeText);
if (view.getId() == R.id.servo_1) {
- ((TextView) v.findViewById(R.id.timeline_box_degree_text)).setText(text.getText());
+ ((TextView) v.findViewById(R.id.timeline_box_degree_text)).setText(text.getText().toString());
}
}
return true;
@@ -651,7 +660,7 @@ public boolean onDrag(View v, DragEvent event) {
View view = (View) event.getLocalState();
TextView text = view.findViewById(R.id.degreeText);
if (view.getId() == R.id.servo_2) {
- ((TextView) v.findViewById(R.id.timeline_box_degree_text)).setText(text.getText());
+ ((TextView) v.findViewById(R.id.timeline_box_degree_text)).setText(text.getText().toString());
}
}
return true;
@@ -664,7 +673,7 @@ public boolean onDrag(View v, DragEvent event) {
View view = (View) event.getLocalState();
TextView text = view.findViewById(R.id.degreeText);
if (view.getId() == R.id.servo_3) {
- ((TextView) v.findViewById(R.id.timeline_box_degree_text)).setText(text.getText());
+ ((TextView) v.findViewById(R.id.timeline_box_degree_text)).setText(text.getText().toString());
}
}
return true;
@@ -677,7 +686,7 @@ public boolean onDrag(View v, DragEvent event) {
View view = (View) event.getLocalState();
TextView text = view.findViewById(R.id.degreeText);
if (view.getId() == R.id.servo_4) {
- ((TextView) v.findViewById(R.id.timeline_box_degree_text)).setText(text.getText());
+ ((TextView) v.findViewById(R.id.timeline_box_degree_text)).setText(text.getText().toString());
}
}
return true;
diff --git a/app/src/main/java/io/pslab/activity/SettingsActivity.java b/app/src/main/java/io/pslab/activity/SettingsActivity.java
index 0c51d75ef..9fbea0fac 100644
--- a/app/src/main/java/io/pslab/activity/SettingsActivity.java
+++ b/app/src/main/java/io/pslab/activity/SettingsActivity.java
@@ -20,6 +20,7 @@
import io.pslab.fragment.AccelerometerSettingsFragment;
import io.pslab.fragment.BaroMeterSettingsFragment;
import io.pslab.fragment.CompassSettingsFragment;
+import io.pslab.fragment.DustSensorSettingsFragment;
import io.pslab.fragment.GyroscopeSettingsFragment;
import io.pslab.fragment.LuxMeterSettingFragment;
import io.pslab.fragment.MultimeterSettingsFragment;
@@ -80,6 +81,9 @@ protected void onCreate(Bundle savedInstanceState) {
case PSLabSensor.COMPASS_CONFIGURATIONS:
fragment = new CompassSettingsFragment();
break;
+ case PSLabSensor.DUSTSENSOR_CONFIGURATIONS:
+ fragment = new DustSensorSettingsFragment();
+ break;
default:
fragment = new SettingsFragment();
break;
diff --git a/app/src/main/java/io/pslab/activity/WaveGeneratorActivity.java b/app/src/main/java/io/pslab/activity/WaveGeneratorActivity.java
index 74fb11f88..10a15525c 100644
--- a/app/src/main/java/io/pslab/activity/WaveGeneratorActivity.java
+++ b/app/src/main/java/io/pslab/activity/WaveGeneratorActivity.java
@@ -9,7 +9,10 @@
import android.graphics.drawable.Drawable;
import android.location.Location;
import android.location.LocationManager;
-import android.os.Build;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.AudioTrack;
+import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
@@ -20,6 +23,7 @@
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.GestureDetector;
@@ -29,7 +33,6 @@
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;
@@ -37,18 +40,23 @@
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
-import android.support.v7.widget.Toolbar;
import com.github.mikephil.charting.charts.LineChart;
+import com.github.mikephil.charting.components.Legend;
+import com.github.mikephil.charting.data.Entry;
+import com.github.mikephil.charting.data.LineData;
+import com.github.mikephil.charting.data.LineDataSet;
+import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
import com.warkiz.widget.IndicatorSeekBar;
+import java.util.ArrayList;
import java.util.Date;
+import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import butterknife.BindView;
import butterknife.ButterKnife;
-import io.pslab.DataFormatter;
import io.pslab.R;
import io.pslab.communication.ScienceLab;
import io.pslab.models.SensorDataBlock;
@@ -178,6 +186,8 @@ public class WaveGeneratorActivity extends AppCompatActivity {
private RelativeLayout pwmModeControls;
private RelativeLayout squareModeControls;
private LineChart previewChart;
+ private boolean isPlayingSound = false;
+ private ProduceSoundTask produceSoundTask;
@SuppressLint("ClickableViewAccessibility")
@Override
@@ -202,6 +212,8 @@ protected void onCreate(Bundle savedInstanceState) {
pwmModeLayout = findViewById(R.id.pwm_mode_layout);
previewChart = findViewById(R.id.chart_preview);
+ waveBtnActive = WaveConst.WAVE1;
+ pwmBtnActive = WaveConst.SQR1;
squareModeControls = findViewById(R.id.square_mode_controls);
pwmModeControls = findViewById(R.id.pwm_mode_controls);
csvLogger = new CSVLogger(getString(R.string.wave_generator));
@@ -414,7 +426,15 @@ public void onClick(View view) {
seekBar.setOnSeekChangeListener(new IndicatorSeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(IndicatorSeekBar seekBar, int progress, float progressFloat, boolean fromUserTouch) {
- String valueText = progress + " " + unit;
+ String valueText;
+ switch (unit) {
+ case "\u00b0":
+ valueText = progress + unit;
+ break;
+ default:
+ valueText = progress + " " + unit;
+ }
+
if (waveMonSelected) {
waveMonPropValueSelect.setText(valueText);
} else {
@@ -445,6 +465,23 @@ public void onStopTrackingTouch(IndicatorSeekBar seekBar) {
setReceivedData();
}
chartInit();
+
+ btnProduceSound.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (isPlayingSound) {
+ btnProduceSound.setText(getResources().getString(R.string.produce_sound_text));
+ produceSoundTask.cancel(true);
+ produceSoundTask = null;
+ isPlayingSound = false;
+ } else {
+ btnProduceSound.setText(getResources().getString(R.string.stop_sound_text));
+ produceSoundTask = new ProduceSoundTask();
+ produceSoundTask.execute();
+ isPlayingSound = true;
+ }
+ }
+ });
}
public void saveWaveConfig() {
@@ -751,6 +788,7 @@ public void selectBtn(WaveConst btn_selected) {
}
prop_active = null;
toggleSeekBtns(false);
+ previewWave();
}
private void selectWaveform(final int waveType) {
@@ -778,6 +816,7 @@ private void selectWaveform(final int waveType) {
}
selectedWaveText.setText(waveFormText);
selectedWaveImg.setImageDrawable(image);
+ previewWave();
}
private void toggleDigitalMode(WaveConst mode) {
@@ -800,11 +839,19 @@ private void toggleDigitalMode(WaveConst mode) {
btnPwmSq4.setEnabled(true);
}
WaveGeneratorCommon.mode_selected = mode;
+ previewWave();
}
private void fetchPropertyValue(WaveConst btnActive, WaveConst property, String unit, TextView propTextView) {
Double value = (double) WaveGeneratorCommon.wave.get(btnActive).get(property);
- String valueText = value.intValue() + " " + unit;
+ String valueText;
+ switch (unit) {
+ case "\u00b0":
+ valueText = value.intValue() + unit;
+ break;
+ default:
+ valueText = value.intValue() + " " + unit;
+ }
propTextView.setText(valueText);
}
@@ -897,11 +944,87 @@ private void setValue() {
WaveGeneratorCommon.wave.get(waveBtnActive).put(prop_active, value);
}
setWave();
+ previewWave();
Double dValue = (double) value;
- String valueText = String.valueOf(dValue.intValue()) + " " + unit;
+ String valueText;
+ switch (unit) {
+ case "\u00b0":
+ valueText = String.valueOf(dValue.intValue()) + unit;
+ break;
+ default:
+ valueText = String.valueOf(dValue.intValue()) + " " + unit;
+ }
activePropTv.setText(valueText);
}
+ private void previewWave() {
+ List dataSets = new ArrayList<>();
+ ArrayList entries = getSamplePoints(false);
+ ArrayList refEntries = getSamplePoints(true);
+ LineDataSet dataSet;
+ LineDataSet refDataSet;
+ if (WaveGeneratorCommon.mode_selected == WaveConst.PWM) {
+ dataSet = new LineDataSet(entries, pwmBtnActive.toString());
+ refDataSet = new LineDataSet(refEntries, getResources().getString(R.string.reference_wave_title));
+ } else {
+ dataSet = new LineDataSet(entries, waveBtnActive.toString());
+ refDataSet = new LineDataSet(refEntries, getResources().getString(R.string.reference_wave_title));
+ }
+ dataSet.setDrawCircles(false);
+ dataSet.setColor(Color.WHITE);
+ refDataSet.setDrawCircles(false);
+ refDataSet.setColor(Color.GRAY);
+ dataSets.add(refDataSet);
+ dataSets.add(dataSet);
+ LineData data = new LineData(dataSets);
+ data.setDrawValues(false);
+ previewChart.setData(data);
+ previewChart.notifyDataSetChanged();
+ previewChart.invalidate();
+ }
+
+ private ArrayList getSamplePoints(boolean isReference) {
+ ArrayList entries = new ArrayList<>();
+ if (WaveGeneratorCommon.mode_selected == WaveConst.PWM) {
+ double freq = (double) WaveGeneratorCommon.wave.get(WaveConst.SQR1).get(WaveConst.FREQUENCY);
+ double duty = ((double) WaveGeneratorCommon.wave.get(pwmBtnActive).get(WaveConst.DUTY)) / 100;
+ double phase = 0;
+ if (pwmBtnActive != WaveConst.SQR1 && !isReference) {
+ phase = (double) WaveGeneratorCommon.wave.get(pwmBtnActive).get(WaveConst.PHASE);
+ }
+ for (int i = 0; i < 5000; i++) {
+ double t = 2 * Math.PI * freq * (i) / 1e6 + phase * Math.PI / 180;
+ double y;
+ if (t % (2 * Math.PI) < 2 * Math.PI * duty) {
+ y = 5;
+ } else {
+ y = -5;
+ }
+ entries.add(new Entry((float) i, (float) y));
+ }
+ } else {
+ double phase = 0;
+ int shape = WaveGeneratorCommon.wave.get(waveBtnActive).get(WaveConst.WAVETYPE);
+ double freq = (double) WaveGeneratorCommon.wave.get(waveBtnActive).get(WaveConst.FREQUENCY);
+
+ if (waveBtnActive != WaveConst.WAVE1 && !isReference) {
+ phase = (double) WaveGeneratorCommon.wave.get(WaveConst.WAVE2).get(WaveConst.PHASE);
+ }
+ if (shape == 1) {
+ for (int i = 0; i < 5000; i++) {
+ float y = (float) (5 * Math.sin(2 * Math.PI * (freq / 1e6) * i + phase * Math.PI / 180));
+ entries.add(new Entry((float) i, y));
+ }
+ } else {
+ for (int i = 0; i < 5000; i++) {
+ float y = (float) ((10 / Math.PI) * (Math.asin(Math.sin(2 * Math.PI * (freq / 1e6) * i + phase * Math.PI / 180))));
+ entries.add(new Entry((float) i, y));
+ }
+ }
+ }
+ return entries;
+ }
+
private void chartInit() {
previewChart.setTouchEnabled(true);
previewChart.setHighlightPerDragEnabled(true);
@@ -912,13 +1035,18 @@ private void chartInit() {
previewChart.setScaleYEnabled(false);
previewChart.setBackgroundColor(Color.BLACK);
previewChart.getDescription().setEnabled(false);
- previewChart.getXAxis().setAxisMaximum(1000);
+ previewChart.getXAxis().setAxisMaximum(5000);
previewChart.getXAxis().setAxisMinimum(0);
previewChart.getXAxis().setTextColor(Color.WHITE);
- previewChart.getAxisLeft().setAxisMaximum(5);
- previewChart.getAxisLeft().setAxisMinimum(-5);
+ previewChart.getAxisLeft().setAxisMaximum(10);
+ previewChart.getAxisLeft().setAxisMinimum(-10);
+ previewChart.getAxisRight().setAxisMaximum(10);
+ previewChart.getAxisRight().setAxisMinimum(-10);
previewChart.fitScreen();
previewChart.invalidate();
+ Legend l = previewChart.getLegend();
+ l.setForm(Legend.LegendForm.LINE);
+ l.setTextColor(Color.WHITE);
}
private void toggleSeekBtns(boolean state) {
@@ -1169,4 +1297,50 @@ public final int getValue() {
return value;
}
}
+
+ private class ProduceSoundTask extends AsyncTask {
+
+ private AudioTrack track;
+
+ @Override
+ protected Void doInBackground(Void... voids) {
+ short[] buffer = new short[1024];
+ track = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, buffer.length, AudioTrack.MODE_STREAM);
+ float angle = 0;
+ float samples[] = new float[1024];
+
+ track.play();
+ double frequency;
+ while (isPlayingSound) {
+ if (WaveGeneratorCommon.mode_selected == WaveConst.SQUARE) {
+ frequency = WaveGeneratorCommon.wave.get(waveBtnActive).get(WaveConst.FREQUENCY);
+ } else {
+ frequency = WaveGeneratorCommon.wave.get(WaveConst.SQR1).get(WaveConst.FREQUENCY);
+ }
+ float increment = (float) ((2 * Math.PI) * frequency / 44100);
+ for (int i = 0; i < samples.length; i++) {
+ samples[i] = (float) Math.sin(angle);
+ if (WaveGeneratorCommon.mode_selected == WaveConst.PWM) {
+ samples[i] = (samples[i] >= 0.0) ? 1 : -1;
+ } else {
+ if (WaveGeneratorCommon.wave.get(waveBtnActive).get(WaveConst.WAVETYPE) == 2) {
+ samples[i] = (float) ((2 / Math.PI) * Math.asin(samples[i]));
+ }
+ }
+ buffer[i] = (short) (samples[i] * Short.MAX_VALUE);
+ angle += increment;
+ }
+ track.write(buffer, 0, buffer.length);
+ }
+ return null;
+ }
+
+ @Override
+ protected void onCancelled() {
+ super.onCancelled();
+ track.flush();
+ track.stop();
+ track.release();
+ }
+ }
}
diff --git a/app/src/main/java/io/pslab/adapters/CheckBoxAdapter.java b/app/src/main/java/io/pslab/adapters/CheckBoxAdapter.java
new file mode 100644
index 000000000..ea0256bbd
--- /dev/null
+++ b/app/src/main/java/io/pslab/adapters/CheckBoxAdapter.java
@@ -0,0 +1,75 @@
+package io.pslab.adapters;
+
+import android.content.Context;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import io.pslab.CheckBoxGetter;
+import io.pslab.R;
+
+
+public class CheckBoxAdapter extends RecyclerView.Adapter {
+
+ private Context boxcontext;
+ private List list = new ArrayList<>();
+
+ public CheckBoxAdapter(Context boxcontext, List list) {
+ this.boxcontext = boxcontext;
+ this.list = list;
+ }
+
+ @Override
+ public CheckBoxHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+
+ View view = LayoutInflater.from(boxcontext).inflate(R.layout.item_checkbox, parent, false);
+ return new CheckBoxHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(final CheckBoxHolder holder, final int position) {
+
+ final CheckBoxGetter check = list.get(position);
+
+ holder.tv_name.setText(check.getName());
+
+ holder.checkBox.setChecked(check.isSelected());
+ holder.checkBox.setTag(list.get(position));
+
+ holder.checkBox.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ CheckBoxGetter check1 = (CheckBoxGetter) holder.checkBox.getTag();
+ check1.setSelected(holder.checkBox.isChecked());
+ list.get(position).setSelected(holder.checkBox.isChecked());
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return list.size();
+ }
+
+ public List getCheckList() {
+ return list;
+ }
+
+ public static class CheckBoxHolder extends RecyclerView.ViewHolder {
+
+ private TextView tv_name;
+ private CheckBox checkBox;
+
+ public CheckBoxHolder(View itemView) {
+ super(itemView);
+ tv_name = itemView.findViewById(R.id.tv_checkbox);
+ checkBox = itemView.findViewById(R.id.checkBox_select);
+ }
+ }
+}
diff --git a/app/src/main/java/io/pslab/adapters/SensorLoggerListAdapter.java b/app/src/main/java/io/pslab/adapters/SensorLoggerListAdapter.java
index 5a0473609..84283b9d2 100644
--- a/app/src/main/java/io/pslab/adapters/SensorLoggerListAdapter.java
+++ b/app/src/main/java/io/pslab/adapters/SensorLoggerListAdapter.java
@@ -135,7 +135,7 @@ public void onBindViewHolder(@NonNull final ViewHolder holder, int position) {
break;
case PSLabSensor.GAS_SENSOR:
holder.sensor.setText(R.string.gas_sensor);
- holder.tileIcon.setImageDrawable(context.getResources().getDrawable(R.drawable.robotic_arm));
+ holder.tileIcon.setImageDrawable(context.getResources().getDrawable(R.drawable.tile_icon_gas));
break;
default:
break;
diff --git a/app/src/main/java/io/pslab/fragment/DustSensorDataFragment.java b/app/src/main/java/io/pslab/fragment/DustSensorDataFragment.java
new file mode 100644
index 000000000..850fdb224
--- /dev/null
+++ b/app/src/main/java/io/pslab/fragment/DustSensorDataFragment.java
@@ -0,0 +1,442 @@
+package io.pslab.fragment;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.location.Location;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.github.anastr.speedviewlib.PointerSpeedometer;
+import com.github.mikephil.charting.charts.LineChart;
+import com.github.mikephil.charting.components.Legend;
+import com.github.mikephil.charting.components.XAxis;
+import com.github.mikephil.charting.components.YAxis;
+import com.github.mikephil.charting.data.Entry;
+import com.github.mikephil.charting.data.LineData;
+import com.github.mikephil.charting.data.LineDataSet;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import butterknife.Unbinder;
+import io.pslab.DataFormatter;
+import io.pslab.R;
+import io.pslab.activity.DustSensorActivity;
+import io.pslab.communication.ScienceLab;
+import io.pslab.models.DustSensorData;
+import io.pslab.models.GasSensorData;
+import io.pslab.models.SensorDataBlock;
+import io.pslab.others.CSVLogger;
+import io.pslab.others.ScienceLabCommon;
+
+import static io.pslab.others.CSVLogger.CSV_DIRECTORY;
+
+public class DustSensorDataFragment extends Fragment {
+
+ @BindView(R.id.dust_sensor_value)
+ TextView dustValue;
+ @BindView(R.id.dust_sensor_status)
+ TextView dustStatus;
+ @BindView(R.id.label_dust_sensor)
+ TextView sensorLabel;
+ @BindView(R.id.chart_dust_sensor)
+ LineChart mChart;
+ @BindView(R.id.dust_sensor)
+ PointerSpeedometer dustSensorMeter;
+
+ // TODO: Support multiple kinds of dust sensors
+ private static int sensorType = 0;
+ private static double highLimit = 4.0;
+ private static int updatePeriod = 1000;
+
+ private DustSensorActivity dustSensorActivity;
+ private View rootView;
+ private Unbinder unbinder;
+ private ScienceLab scienceLab;
+ private YAxis y;
+ private Timer graphTimer;
+ private ArrayList entries;
+ private long startTime;
+ private long timeElapsed;
+ private long previousTimeElapsed = (System.currentTimeMillis() - startTime) / updatePeriod;
+ private long block;
+ private GasSensorData sensorData;
+ private boolean returningFromPause = false;
+ private int turns = 0;
+ private ArrayList recordedDustSensorArray;
+
+ public static DustSensorDataFragment newInstance() {
+ return new DustSensorDataFragment();
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ startTime = System.currentTimeMillis();
+ dustSensorActivity = (DustSensorActivity) getActivity();
+ }
+
+ public static void setParameters(double highLimit, int updatePeriod, String type) {
+ DustSensorDataFragment.highLimit = highLimit;
+ DustSensorDataFragment.updatePeriod = updatePeriod;
+ DustSensorDataFragment.sensorType = Integer.valueOf(type);
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ rootView = inflater.inflate(R.layout.fragment_dust_sensor, container, false);
+ unbinder = ButterKnife.bind(this, rootView);
+ scienceLab = ScienceLabCommon.scienceLab;
+ entries = new ArrayList<>();
+ setupInstruments();
+ return rootView;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (dustSensorActivity.playingData) {
+ sensorLabel.setText(getResources().getString(R.string.baro_meter));
+ recordedDustSensorArray = new ArrayList<>();
+ resetInstrumentData();
+ playRecordedData();
+ } else if (dustSensorActivity.viewingData) {
+ sensorLabel.setText(getResources().getString(R.string.baro_meter));
+ recordedDustSensorArray = new ArrayList<>();
+ resetInstrumentData();
+ plotAllRecordedData();
+ } else if (!dustSensorActivity.isRecording) {
+ updateGraphs();
+ entries.clear();
+ mChart.clear();
+ mChart.invalidate();
+ } else if (returningFromPause) {
+ updateGraphs();
+ }
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ if (graphTimer != null) {
+ graphTimer.cancel();
+ }
+ unbinder.unbind();
+ }
+
+ private void plotAllRecordedData() {
+ recordedDustSensorArray.addAll(dustSensorActivity.recordedDustSensorData);
+ if (recordedDustSensorArray.size() != 0) {
+ for (DustSensorData d : recordedDustSensorArray) {
+ Entry entry = new Entry((float) (d.getTime() - d.getBlock()) / 1000, d.getPpmValue());
+ entries.add(entry);
+ dustSensorMeter.setWithTremble(false);
+ float ppm = d.getPpmValue();
+ dustSensorMeter.setSpeedAt(ppm);
+ dustSensorMeter.setPointerColor(ppm > highLimit ? Color.WHITE : Color.RED);
+ dustValue.setText(String.valueOf(String.format(Locale.getDefault(), "%.2f", ppm)));
+ String status = ppm > highLimit ? "Good" : "Bad";
+ dustStatus.setText(status);
+ }
+ y.setAxisMaximum(5);
+ y.setAxisMinimum(0);
+ y.setLabelCount(10);
+
+ LineDataSet dataSet = new LineDataSet(entries, getString(R.string.baro_unit));
+ dataSet.setDrawCircles(false);
+ dataSet.setDrawValues(false);
+ dataSet.setLineWidth(2);
+ LineData data = new LineData(dataSet);
+
+ mChart.setData(data);
+ mChart.notifyDataSetChanged();
+ mChart.setVisibleXRangeMaximum(80);
+ mChart.moveViewToX(data.getEntryCount());
+ mChart.invalidate();
+ }
+ }
+
+ private void playRecordedData() {
+ recordedDustSensorArray.addAll(dustSensorActivity.recordedDustSensorData);
+ try {
+ if (recordedDustSensorArray.size() > 1) {
+ DustSensorData i = recordedDustSensorArray.get(1);
+ long timeGap = i.getTime() - i.getBlock();
+ processRecordedData(timeGap);
+ } else {
+ processRecordedData(0);
+ }
+ } catch (IllegalArgumentException e) {
+ Toast.makeText(getActivity(),
+ getActivity().getResources().getString(R.string.no_data_fetched), Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private void processRecordedData(long timeGap) {
+ final Handler handler = new Handler();
+ if (graphTimer != null) {
+ graphTimer.cancel();
+ } else {
+ graphTimer = new Timer();
+ }
+ graphTimer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (dustSensorActivity.playingData) {
+ try {
+ DustSensorData d = recordedDustSensorArray.get(turns);
+ turns++;
+ float ppm = d.getPpmValue();
+ dustSensorMeter.setPointerColor(ppm > highLimit ? Color.WHITE : Color.RED);
+ dustValue.setText(String.valueOf(String.format(Locale.getDefault(), "%.2f", ppm)));
+ String status = ppm > highLimit ? "Good" : "Bad";
+ dustStatus.setText(status);
+
+ y.setAxisMaximum(5);
+ y.setAxisMinimum(0);
+ y.setLabelCount(10);
+ dustSensorMeter.setWithTremble(false);
+ dustSensorMeter.setSpeedAt(ppm);
+
+ Entry entry = new Entry((float) (d.getTime() - d.getBlock()) / 1000, d.getPpmValue());
+ entries.add(entry);
+
+ LineDataSet dataSet = new LineDataSet(entries, getString(R.string.baro_unit));
+ dataSet.setDrawCircles(false);
+ dataSet.setDrawValues(false);
+ dataSet.setLineWidth(2);
+ LineData data = new LineData(dataSet);
+
+ mChart.setData(data);
+ mChart.notifyDataSetChanged();
+ mChart.setVisibleXRangeMaximum(80);
+ mChart.moveViewToX(data.getEntryCount());
+ mChart.invalidate();
+ } catch (IndexOutOfBoundsException e) {
+ graphTimer.cancel();
+ graphTimer = null;
+ turns = 0;
+ dustSensorActivity.playingData = false;
+ dustSensorActivity.startedPlay = false;
+ dustSensorActivity.invalidateOptionsMenu();
+ }
+ }
+ }
+ });
+ }
+ }, 0, timeGap);
+ }
+
+ public void playData() {
+ resetInstrumentData();
+ dustSensorActivity.startedPlay = true;
+ try {
+ if (recordedDustSensorArray.size() > 1) {
+ DustSensorData i = recordedDustSensorArray.get(1);
+ long timeGap = i.getTime() - i.getBlock();
+ processRecordedData(timeGap);
+ } else {
+ processRecordedData(0);
+ }
+ } catch (IllegalArgumentException e) {
+ Toast.makeText(getActivity(),
+ getActivity().getResources().getString(R.string.no_data_fetched), Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ public void stopData() {
+ if (graphTimer != null) {
+ graphTimer.cancel();
+ graphTimer = null;
+ }
+ recordedDustSensorArray.clear();
+ entries.clear();
+ plotAllRecordedData();
+ dustSensorActivity.startedPlay = false;
+ dustSensorActivity.playingData = false;
+ turns = 0;
+ dustSensorActivity.invalidateOptionsMenu();
+ }
+
+ public void saveGraph() {
+ dustSensorActivity.csvLogger.prepareLogFile();
+ dustSensorActivity.csvLogger.writeMetaData(getResources().getString(R.string.gas_sensor));
+ dustSensorActivity.csvLogger.writeCSVFile("Timestamp,DateTime,ppmValue,Latitude,Longitude");
+ for (DustSensorData dustSensorData : dustSensorActivity.recordedDustSensorData) {
+ dustSensorActivity.csvLogger.writeCSVFile(dustSensorData.getTime() + ","
+ + CSVLogger.FILE_NAME_FORMAT.format(new Date(dustSensorData.getTime())) + ","
+ + dustSensorData.getPpmValue() + ","
+ + dustSensorData.getLat() + ","
+ + dustSensorData.getLon());
+ }
+ View view = rootView.findViewById(R.id.gas_sensor_linearlayout);
+ view.setDrawingCacheEnabled(true);
+ Bitmap b = view.getDrawingCache();
+ try {
+ b.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(Environment.getExternalStorageDirectory().getAbsolutePath() +
+ File.separator + CSV_DIRECTORY + File.separator + dustSensorActivity.getSensorName() +
+ File.separator + CSVLogger.FILE_NAME_FORMAT.format(new Date()) + "_graph.jpg"));
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ if (graphTimer != null) {
+ returningFromPause = true;
+ graphTimer.cancel();
+ graphTimer = null;
+ if (dustSensorActivity.playingData) {
+ dustSensorActivity.finish();
+ }
+ }
+ }
+
+ private void updateGraphs() {
+ final Handler handler = new Handler();
+ if (graphTimer != null) {
+ graphTimer.cancel();
+ }
+ graphTimer = new Timer();
+ graphTimer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ handler.post(() -> {
+ try {
+ visualizeData();
+ } catch (NullPointerException e) {
+ }
+ });
+ }
+ }, 0, 1000);
+ }
+
+ private void writeLogToFile(long timestamp, float ppmValue) {
+ if (getActivity() != null && dustSensorActivity.isRecording) {
+ if (dustSensorActivity.writeHeaderToFile) {
+ dustSensorActivity.csvLogger.prepareLogFile();
+ dustSensorActivity.csvLogger.writeCSVFile("Timestamp,DateTime,ppmValue,Latitude,Longitude");
+ block = timestamp;
+ dustSensorActivity.recordSensorDataBlockID(new SensorDataBlock(timestamp, dustSensorActivity.getSensorName()));
+ dustSensorActivity.writeHeaderToFile = !dustSensorActivity.writeHeaderToFile;
+ }
+ if (dustSensorActivity.addLocation && dustSensorActivity.gpsLogger.isGPSEnabled()) {
+ String dateTime = CSVLogger.FILE_NAME_FORMAT.format(new Date(timestamp));
+ Location location = dustSensorActivity.gpsLogger.getDeviceLocation();
+ dustSensorActivity.csvLogger.writeCSVFile(timestamp + "," + dateTime + ","
+ + ppmValue + "," + location.getLatitude() + "," + location.getLongitude());
+ sensorData = new GasSensorData(timestamp, block, ppmValue, location.getLatitude(), location.getLongitude());
+ } else {
+ String dateTime = CSVLogger.FILE_NAME_FORMAT.format(new Date(timestamp));
+ dustSensorActivity.csvLogger.writeCSVFile(timestamp + "," + dateTime + ","
+ + ppmValue + ",0.0,0.0");
+ sensorData = new GasSensorData(timestamp, block, ppmValue, 0.0, 0.0);
+ }
+ dustSensorActivity.recordSensorData(sensorData);
+ } else {
+ dustSensorActivity.writeHeaderToFile = true;
+ }
+ }
+
+ private void visualizeData() {
+ if (scienceLab.isConnected()) {
+ double ppm = scienceLab.getVoltage("CH1", 1);
+ dustSensorMeter.setPointerColor(ppm > highLimit ? Color.WHITE : Color.RED);
+ dustValue.setText(String.valueOf(String.format(Locale.getDefault(), "%.2f", ppm)));
+ String status = ppm > highLimit ? "Good" : "Bad";
+ dustStatus.setText(status);
+ dustSensorMeter.setWithTremble(false);
+ dustSensorMeter.setSpeedAt((float) ppm);
+ timeElapsed = ((System.currentTimeMillis() - startTime) / updatePeriod);
+ if (timeElapsed != previousTimeElapsed) {
+ previousTimeElapsed = timeElapsed;
+ Entry entry = new Entry((float) timeElapsed, (float) ppm);
+ entries.add(entry);
+ writeLogToFile(System.currentTimeMillis(), (float) ppm);
+ LineDataSet dataSet = new LineDataSet(entries, getString(R.string.gas_sensor_unit));
+ dataSet.setDrawCircles(false);
+ dataSet.setDrawValues(false);
+ dataSet.setLineWidth(2);
+ LineData data = new LineData(dataSet);
+
+ mChart.setData(data);
+ mChart.notifyDataSetChanged();
+ mChart.setVisibleXRangeMaximum(80);
+ mChart.moveViewToX(data.getEntryCount());
+ mChart.invalidate();
+ }
+ } else {
+ Toast.makeText(getContext(), R.string.not_connected, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private void setupInstruments() {
+ dustSensorMeter.setMaxSpeed(5);
+ XAxis x = mChart.getXAxis();
+ this.y = mChart.getAxisLeft();
+ YAxis y2 = mChart.getAxisRight();
+
+ mChart.setTouchEnabled(true);
+ mChart.setHighlightPerDragEnabled(true);
+ mChart.setDragEnabled(true);
+ mChart.setScaleEnabled(true);
+ mChart.setDrawGridBackground(false);
+ mChart.setPinchZoom(true);
+ mChart.setScaleYEnabled(true);
+ mChart.setBackgroundColor(Color.BLACK);
+ mChart.getDescription().setEnabled(false);
+
+ LineData data = new LineData();
+ mChart.setData(data);
+
+ Legend l = mChart.getLegend();
+ l.setForm(Legend.LegendForm.LINE);
+ l.setTextColor(Color.WHITE);
+
+ x.setTextColor(Color.WHITE);
+ x.setDrawGridLines(true);
+ x.setAvoidFirstLastClipping(true);
+
+ y.setTextColor(Color.WHITE);
+ y.setAxisMaximum(5);
+ y.setAxisMinimum(0);
+ y.setDrawGridLines(true);
+ y.setLabelCount(10);
+
+ y2.setDrawGridLines(false);
+ y2.setMaxWidth(0);
+ }
+
+ private void resetInstrumentData() {
+ startTime = System.currentTimeMillis();
+ dustValue.setText(DataFormatter.formatDouble(0, DataFormatter.LOW_PRECISION_FORMAT));
+ dustStatus.setText(getString(R.string.unknown));
+ dustSensorMeter.setSpeedAt(0);
+ dustSensorMeter.setWithTremble(false);
+ entries.clear();
+ }
+
+}
diff --git a/app/src/main/java/io/pslab/fragment/DustSensorSettingsFragment.java b/app/src/main/java/io/pslab/fragment/DustSensorSettingsFragment.java
new file mode 100644
index 000000000..5c5f2179b
--- /dev/null
+++ b/app/src/main/java/io/pslab/fragment/DustSensorSettingsFragment.java
@@ -0,0 +1,115 @@
+package io.pslab.fragment;
+
+import android.annotation.SuppressLint;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.support.v7.preference.CheckBoxPreference;
+import android.support.v7.preference.EditTextPreference;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.PreferenceFragmentCompat;
+import android.support.v7.preference.PreferenceManager;
+import android.widget.Toast;
+
+import io.pslab.R;
+import io.pslab.others.PSLabPermission;
+
+public class DustSensorSettingsFragment extends PreferenceFragmentCompat implements SharedPreferences.OnSharedPreferenceChangeListener {
+
+ public static final String KEY_INCLUDE_LOCATION = "include_location_sensor_data";
+ public static final String KEY_UPDATE_PERIOD = "setting_dust_update_period";
+ public static final String KEY_HIGH_LIMIT = "setting_dust_high_limit";
+ public static final String KEY_DUST_SENSOR_TYPE = "setting_dust_sensor_type";
+
+ private PSLabPermission psLabPermission;
+
+ private EditTextPreference updatePeriodPref;
+ private EditTextPreference highLimitPref;
+ private CheckBoxPreference locationPreference;
+ private ListPreference sensorTypePreference;
+ private SharedPreferences sharedPref;
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ setPreferencesFromResource(R.xml.dust_sensor_settings, rootKey);
+ updatePeriodPref = (EditTextPreference) getPreferenceScreen().findPreference(KEY_UPDATE_PERIOD);
+ highLimitPref = (EditTextPreference) getPreferenceScreen().findPreference(KEY_HIGH_LIMIT);
+ locationPreference = (CheckBoxPreference) getPreferenceScreen().findPreference(KEY_INCLUDE_LOCATION);
+ sensorTypePreference = (ListPreference) getPreferenceScreen().findPreference(KEY_DUST_SENSOR_TYPE);
+ sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity());
+
+ psLabPermission = PSLabPermission.getInstance();
+ if (!psLabPermission.checkPermissions(getActivity(), PSLabPermission.MAP_PERMISSION)) {
+ SharedPreferences.Editor editor = sharedPref.edit();
+ editor.putBoolean(DustSensorSettingsFragment.KEY_INCLUDE_LOCATION, true);
+ editor.apply();
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ locationPreference.setChecked(sharedPref.getBoolean(KEY_INCLUDE_LOCATION, true));
+ updatePeriodPref.setSummary(updatePeriodPref.getText() + " ms");
+ highLimitPref.setSummary(highLimitPref.getText() + " PPM");
+ sensorTypePreference.setSummary(sensorTypePreference.getEntry());
+ getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
+ }
+
+ @SuppressLint("ApplySharedPref")
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
+ switch (s) {
+ case KEY_INCLUDE_LOCATION:
+ if (locationPreference.isChecked()) {
+ psLabPermission.checkPermissions(
+ getActivity(), PSLabPermission.MAP_PERMISSION);
+ }
+ break;
+ case KEY_UPDATE_PERIOD:
+ try {
+ int updatePeriod = Integer.parseInt(updatePeriodPref.getText());
+ if (updatePeriod > 1000 || updatePeriod < 100) {
+ throw new NumberFormatException();
+ } else {
+ updatePeriodPref.setSummary(updatePeriod + " ms");
+ }
+ } catch (NumberFormatException e) {
+ Toast.makeText(getActivity(), getActivity().getResources().getString(R.string.update_period_msg), Toast.LENGTH_SHORT).show();
+ updatePeriodPref.setSummary("1000 ms");
+ updatePeriodPref.setText("1000");
+ SharedPreferences.Editor editor = sharedPref.edit();
+ editor.putString(s, "1000");
+ editor.commit();
+ }
+ break;
+ case KEY_HIGH_LIMIT:
+ try {
+ double highLimit = Double.parseDouble(highLimitPref.getText());
+ if (highLimit > 5.0 || highLimit < 0.0) {
+ throw new NumberFormatException();
+ } else {
+ highLimitPref.setSummary(String.valueOf(highLimit) + " PPM");
+ }
+ } catch (NumberFormatException e) {
+ Toast.makeText(getActivity(), getActivity().getResources().getString(R.string.high_limit_msg), Toast.LENGTH_SHORT).show();
+ highLimitPref.setSummary("4.0 V");
+ highLimitPref.setText("4.0");
+ SharedPreferences.Editor editor = sharedPref.edit();
+ editor.putString(KEY_HIGH_LIMIT, "4.0");
+ editor.commit();
+ }
+ break;
+ case KEY_DUST_SENSOR_TYPE:
+ sensorTypePreference.setSummary(sensorTypePreference.getEntry());
+ break;
+ default:
+ break;
+ }
+ }
+}
diff --git a/app/src/main/java/io/pslab/fragment/GasSensorDataFragment.java b/app/src/main/java/io/pslab/fragment/GasSensorDataFragment.java
index b5a1aba6c..10060b019 100644
--- a/app/src/main/java/io/pslab/fragment/GasSensorDataFragment.java
+++ b/app/src/main/java/io/pslab/fragment/GasSensorDataFragment.java
@@ -91,6 +91,9 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c
rootView = inflater.inflate(R.layout.fragment_gas_sensor, container, false);
unbinder = ButterKnife.bind(this, rootView);
scienceLab = ScienceLabCommon.scienceLab;
+ if (!scienceLab.isConnected()) {
+ Toast.makeText(getContext(), R.string.not_connected, Toast.LENGTH_SHORT).show();
+ }
entries = new ArrayList<>();
setupInstruments();
return rootView;
@@ -366,8 +369,6 @@ private void visualizeData() {
mChart.moveViewToX(data.getEntryCount());
mChart.invalidate();
}
- } else {
- Toast.makeText(getContext(), R.string.not_connected, Toast.LENGTH_SHORT).show();
}
}
diff --git a/app/src/main/java/io/pslab/fragment/InstrumentsFragment.java b/app/src/main/java/io/pslab/fragment/InstrumentsFragment.java
index 37d3daf8c..6ae4e12d7 100644
--- a/app/src/main/java/io/pslab/fragment/InstrumentsFragment.java
+++ b/app/src/main/java/io/pslab/fragment/InstrumentsFragment.java
@@ -18,6 +18,7 @@
import io.pslab.activity.AccelerometerActivity;
import io.pslab.activity.BarometerActivity;
import io.pslab.activity.CompassActivity;
+import io.pslab.activity.DustSensorActivity;
import io.pslab.activity.GasSensorActivity;
import io.pslab.activity.GyroscopeActivity;
import io.pslab.activity.LogicalAnalyzerActivity;
@@ -59,73 +60,74 @@ public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGrou
context = getActivity().getApplicationContext();
applicationItemList = new ArrayList<>();
applicationAdapter = new ApplicationAdapter(context, applicationItemList,
- new ApplicationAdapter.OnItemClickListener() {
- @Override
- public void onItemClick(ApplicationItem item) {
- Intent intent;
- switch (item.getApplicationName()) {
- case "Oscilloscope":
- intent = new Intent(context, OscilloscopeActivity.class);
- intent.putExtra("who", "Instruments");
- startActivity(intent);
- break;
- case "Multimeter":
- intent = new Intent(context, MultimeterActivity.class);
- startActivity(intent);
- break;
- case "Logic Analyzer":
- intent = new Intent(context, LogicalAnalyzerActivity.class);
- startActivity(intent);
- break;
- case "Sensors":
- intent = new Intent(context, SensorActivity.class);
- startActivity(intent);
- break;
- case "Wave Generator":
- intent = new Intent(context, WaveGeneratorActivity.class);
- startActivity(intent);
- break;
- case "Power Source":
- intent = new Intent(context, PowerSourceActivity.class);
- startActivity(intent);
- break;
- case PSLabSensor.LUXMETER:
- intent = new Intent(context, LuxMeterActivity.class);
- startActivity(intent);
- break;
- case "Accelerometer":
- intent = new Intent(context, AccelerometerActivity.class);
- startActivity(intent);
- break;
- case PSLabSensor.BAROMETER:
- intent = new Intent(context, BarometerActivity.class);
- startActivity(intent);
- break;
- case "Compass":
- intent = new Intent(context, CompassActivity.class);
- startActivity(intent);
- break;
- case "Gyroscope":
- intent = new Intent(context, GyroscopeActivity.class);
- startActivity(intent);
- break;
- case "Thermometer":
- intent = new Intent(context, ThermometerActivity.class);
- startActivity(intent);
- break;
- case "Robotic Arm":
- intent = new Intent(context, RoboticArmActivity.class);
- startActivity(intent);
- break;
- case "Gas Sensor":
- intent = new Intent(context, GasSensorActivity.class);
- startActivity(intent);
- break;
- default:
- break;
- }
-
+ item -> {
+ Intent intent;
+ switch (item.getApplicationName()) {
+ case "Oscilloscope":
+ intent = new Intent(context, OscilloscopeActivity.class);
+ intent.putExtra("who", "Instruments");
+ startActivity(intent);
+ break;
+ case "Multimeter":
+ intent = new Intent(context, MultimeterActivity.class);
+ startActivity(intent);
+ break;
+ case "Logic Analyzer":
+ intent = new Intent(context, LogicalAnalyzerActivity.class);
+ startActivity(intent);
+ break;
+ case "Sensors":
+ intent = new Intent(context, SensorActivity.class);
+ startActivity(intent);
+ break;
+ case "Wave Generator":
+ intent = new Intent(context, WaveGeneratorActivity.class);
+ startActivity(intent);
+ break;
+ case "Power Source":
+ intent = new Intent(context, PowerSourceActivity.class);
+ startActivity(intent);
+ break;
+ case PSLabSensor.LUXMETER:
+ intent = new Intent(context, LuxMeterActivity.class);
+ startActivity(intent);
+ break;
+ case "Accelerometer":
+ intent = new Intent(context, AccelerometerActivity.class);
+ startActivity(intent);
+ break;
+ case PSLabSensor.BAROMETER:
+ intent = new Intent(context, BarometerActivity.class);
+ startActivity(intent);
+ break;
+ case "Compass":
+ intent = new Intent(context, CompassActivity.class);
+ startActivity(intent);
+ break;
+ case "Gyroscope":
+ intent = new Intent(context, GyroscopeActivity.class);
+ startActivity(intent);
+ break;
+ case "Thermometer":
+ intent = new Intent(context, ThermometerActivity.class);
+ startActivity(intent);
+ break;
+ case "Robotic Arm":
+ intent = new Intent(context, RoboticArmActivity.class);
+ startActivity(intent);
+ break;
+ case "Gas Sensor":
+ intent = new Intent(context, GasSensorActivity.class);
+ startActivity(intent);
+ break;
+ case "Dust Sensor":
+ intent = new Intent(context, DustSensorActivity.class);
+ startActivity(intent);
+ break;
+ default:
+ break;
}
+
});
int rows = context.getResources().getConfiguration().orientation
== Configuration.ORIENTATION_PORTRAIT ? 1 : 2;
@@ -167,8 +169,8 @@ protected Void doInBackground(Void... params) {
R.string.gyroscope_description,
R.string.thermometer_desc,
R.string.robotic_arm_description,
- R.string.gas_sensor_description
-
+ R.string.gas_sensor_description,
+ R.string.dust_sensor_description
};
applicationItemList.add(new ApplicationItem(
@@ -211,7 +213,10 @@ protected Void doInBackground(Void... params) {
getResources().getString(R.string.robotic_arm), R.drawable.robotic_arm, getResources().getString(descriptions[12])
));
applicationItemList.add(new ApplicationItem(
- getResources().getString(R.string.gas_sensor), R.drawable.robotic_arm, getResources().getString(descriptions[13])
+ getResources().getString(R.string.gas_sensor), R.drawable.tile_icon_gas, getResources().getString(descriptions[13])
+ ));
+ applicationItemList.add(new ApplicationItem(
+ getResources().getString(R.string.dust_sensor), R.drawable.tile_icon_gas, getResources().getString(descriptions[14])
));
return null;
}
diff --git a/app/src/main/java/io/pslab/interfaces/sensorloggers/DustSensorRecordables.java b/app/src/main/java/io/pslab/interfaces/sensorloggers/DustSensorRecordables.java
new file mode 100644
index 000000000..d4bbbe4c5
--- /dev/null
+++ b/app/src/main/java/io/pslab/interfaces/sensorloggers/DustSensorRecordables.java
@@ -0,0 +1,13 @@
+package io.pslab.interfaces.sensorloggers;
+
+import io.pslab.models.DustSensorData;
+import io.realm.RealmResults;
+
+public interface DustSensorRecordables {
+
+ DustSensorData getDustSensorData(long timeStamp);
+ void clearAllDustSensorRecords();
+ void clearBlockOfDustSensorRecords(long block);
+ RealmResults getAllDustSensorRecords();
+ RealmResults getBlockOfDustSensorRecords(long block);
+}
diff --git a/app/src/main/java/io/pslab/models/DustSensorData.java b/app/src/main/java/io/pslab/models/DustSensorData.java
new file mode 100644
index 000000000..b337d8b52
--- /dev/null
+++ b/app/src/main/java/io/pslab/models/DustSensorData.java
@@ -0,0 +1,68 @@
+package io.pslab.models;
+
+import io.realm.RealmObject;
+import io.realm.annotations.PrimaryKey;
+
+public class DustSensorData extends RealmObject {
+
+ @PrimaryKey
+ private long time;
+ private long block;
+ private float ppmValue;
+ private double lat, lon;
+
+ public DustSensorData() { /**/ }
+
+ public DustSensorData(long time, long block, float ppmValue, double lat, double lon) {
+ this.time = time;
+ this.block = block;
+ this.ppmValue = ppmValue;
+ this.lat = lat;
+ this.lon = lon;
+ }
+
+ public long getTime() {
+ return time;
+ }
+
+ public void setTime(long time) {
+ this.time = time;
+ }
+
+ public long getBlock() {
+ return block;
+ }
+
+ public void setBlock(long block) {
+ this.block = block;
+ }
+
+ public float getPpmValue() {
+ return ppmValue;
+ }
+
+ public void setPpmValue(float ppmValue) {
+ this.ppmValue = ppmValue;
+ }
+
+ public double getLat() {
+ return lat;
+ }
+
+ public void setLat(double lat) {
+ this.lat = lat;
+ }
+
+ public double getLon() {
+ return lon;
+ }
+
+ public void setLon(double lon) {
+ this.lon = lon;
+ }
+
+ @Override
+ public String toString() {
+ return "Block - " + block + ", Time - " + time + ", PPM value - " + ppmValue + ", Lat - " + lat + ", Lon - " + lon;
+ }
+}
diff --git a/app/src/main/java/io/pslab/models/PSLabSensor.java b/app/src/main/java/io/pslab/models/PSLabSensor.java
index 32a107baf..d28e58832 100644
--- a/app/src/main/java/io/pslab/models/PSLabSensor.java
+++ b/app/src/main/java/io/pslab/models/PSLabSensor.java
@@ -45,6 +45,7 @@
import io.pslab.fragment.AccelerometerDataFragment;
import io.pslab.fragment.BaroMeterDataFragment;
import io.pslab.fragment.CompassDataFragment;
+import io.pslab.fragment.DustSensorDataFragment;
import io.pslab.fragment.GasSensorDataFragment;
import io.pslab.fragment.GyroscopeDataFragment;
import io.pslab.fragment.LuxMeterDataFragment;
@@ -107,6 +108,8 @@ public abstract class PSLabSensor extends AppCompatActivity {
public static final String THERMOMETER = "Thermometer";
public static final String THERMOMETER_CONFIGURATIONS = "Thermometer Configurations";
public static final String THERMOMETER_DATA_FORMAT = "%.2f";
+ public static final String DUSTSENSOR = "Dust Sensor";
+ public static final String DUSTSENSOR_CONFIGURATIONS = "Dust Sensor Configurations";
public static final String ROBOTIC_ARM = "Robotic Arm";
public static final String WAVE_GENERATOR = "Wave Generator";
public static final String OSCILLOSCOPE = "Oscilloscope";
@@ -351,6 +354,8 @@ public boolean onOptionsItemSelected(MenuItem item) {
} else if (getSensorFragment() instanceof GasSensorDataFragment){
((GasSensorDataFragment) getSupportFragmentManager()
.findFragmentByTag(getSensorName())).playData();
+ } else if (getSensorFragment() instanceof DustSensorDataFragment) {
+ ((DustSensorDataFragment) getSupportFragmentManager().findFragmentByTag(getSensorName())).playData();
}
}
invalidateOptionsMenu();
@@ -377,6 +382,9 @@ public boolean onOptionsItemSelected(MenuItem item) {
} else if (getSensorFragment() instanceof GasSensorDataFragment){
((GasSensorDataFragment) getSupportFragmentManager()
.findFragmentByTag(getSensorName())).stopData();
+
+ } else if (getSensorFragment() instanceof DustSensorDataFragment) {
+ ((DustSensorDataFragment) getSupportFragmentManager().findFragmentByTag(getSensorName())).stopData();
}
break;
case R.id.show_map:
@@ -424,6 +432,8 @@ public boolean onOptionsItemSelected(MenuItem item) {
} else if (getSensorFragment() instanceof GasSensorDataFragment){
((GasSensorDataFragment) getSupportFragmentManager()
.findFragmentByTag(getSensorName())).saveGraph();
+ } else if (getSensorFragment() instanceof DustSensorDataFragment) {
+ ((DustSensorDataFragment) getSupportFragmentManager().findFragmentByTag(getSensorName())).saveGraph();
}
break;
case android.R.id.home:
diff --git a/app/src/main/java/io/pslab/others/LocalDataLog.java b/app/src/main/java/io/pslab/others/LocalDataLog.java
index 51089115e..39d55fc6d 100644
--- a/app/src/main/java/io/pslab/others/LocalDataLog.java
+++ b/app/src/main/java/io/pslab/others/LocalDataLog.java
@@ -3,6 +3,7 @@
import io.pslab.interfaces.sensorloggers.AccelerometerRecordables;
import io.pslab.interfaces.sensorloggers.BaroMeterRecordables;
import io.pslab.interfaces.sensorloggers.CompassRecordables;
+import io.pslab.interfaces.sensorloggers.DustSensorRecordables;
import io.pslab.interfaces.sensorloggers.GasSensorRecordables;
import io.pslab.interfaces.sensorloggers.GyroscopeRecordables;
import io.pslab.interfaces.sensorloggers.LogicAnalyzerRecordables;
@@ -17,6 +18,7 @@
import io.pslab.models.AccelerometerData;
import io.pslab.models.BaroData;
import io.pslab.models.CompassData;
+import io.pslab.models.DustSensorData;
import io.pslab.models.GasSensorData;
import io.pslab.models.GyroData;
import io.pslab.models.LogicAnalyzerData;
@@ -36,7 +38,7 @@
* Created by Padmal on 11/5/18.
*/
-public class LocalDataLog implements LuxMeterRecordables, BaroMeterRecordables, SensorRecordables, CompassRecordables, AccelerometerRecordables, GyroscopeRecordables, ThermometerRecordables, ServoRecordables, WaveGeneratorRecordables, OscilloscopeRecordables, PowerSourceRecordables, MultimeterRecordables, LogicAnalyzerRecordables, GasSensorRecordables {
+public class LocalDataLog implements DustSensorRecordables, LuxMeterRecordables, BaroMeterRecordables, SensorRecordables, CompassRecordables, AccelerometerRecordables, GyroscopeRecordables, ThermometerRecordables, ServoRecordables, WaveGeneratorRecordables, OscilloscopeRecordables, PowerSourceRecordables, MultimeterRecordables, LogicAnalyzerRecordables, GasSensorRecordables {
private static LocalDataLog instance;
private final Realm realm;
@@ -579,4 +581,39 @@ public RealmResults getBlockOfGasSensorRecords(long block) {
.equalTo("block", block)
.findAll();
}
+
+ /***********************************************************************************************
+ * Dust Sensor Section
+ ***********************************************************************************************/
+ @Override
+ public DustSensorData getDustSensorData(long timestamp) {
+ return realm.where(DustSensorData.class).equalTo("time", timestamp).findFirst();
+ }
+
+ @Override
+ public void clearAllDustSensorRecords() {
+ realm.beginTransaction();
+ realm.delete(LuxData.class);
+ realm.commitTransaction();
+ }
+
+ @Override
+ public void clearBlockOfDustSensorRecords(long block) {
+ realm.beginTransaction();
+ RealmResults data = getBlockOfDustSensorRecords(block);
+ data.deleteAllFromRealm();
+ realm.commitTransaction();
+ }
+
+ @Override
+ public RealmResults getAllDustSensorRecords() {
+ return realm.where(DustSensorData.class).findAll();
+ }
+
+ @Override
+ public RealmResults getBlockOfDustSensorRecords(long block) {
+ return realm.where(DustSensorData.class)
+ .equalTo("block", block)
+ .findAll();
+ }
}
diff --git a/app/src/main/res/drawable-xxhdpi/tile_icon_gas.xml b/app/src/main/res/drawable-xxhdpi/tile_icon_gas.xml
new file mode 100644
index 000000000..47ab25e20
--- /dev/null
+++ b/app/src/main/res/drawable-xxhdpi/tile_icon_gas.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_create_config.xml b/app/src/main/res/layout/activity_create_config.xml
index ea122dad8..a7ce15e1d 100644
--- a/app/src/main/res/layout/activity_create_config.xml
+++ b/app/src/main/res/layout/activity_create_config.xml
@@ -12,84 +12,86 @@
android:layout_height="wrap_content"
android:layout_below="@+id/top_app_bar_layout">
-
+ android:orientation="vertical"
+ android:padding="@dimen/config_activity_padding">
-
+ android:orientation="horizontal">
-
+
-
+
-
+
-
+ android:orientation="horizontal">
+
+
+
+
+
+
+
-
-
+ android:layout_height="@dimen/create_config_recycler_view"
+ android:layout_marginTop="@dimen/create_config_margin1" />
-
+
diff --git a/app/src/main/res/layout/fragment_dust_sensor.xml b/app/src/main/res/layout/fragment_dust_sensor.xml
new file mode 100644
index 000000000..24c7b3c5c
--- /dev/null
+++ b/app/src/main/res/layout/fragment_dust_sensor.xml
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_checkbox.xml b/app/src/main/res/layout/item_checkbox.xml
new file mode 100644
index 000000000..f51ba6999
--- /dev/null
+++ b/app/src/main/res/layout/item_checkbox.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/servo_controller_layout.xml b/app/src/main/res/layout/servo_controller_layout.xml
index e7b42fa8b..66d291512 100644
--- a/app/src/main/res/layout/servo_controller_layout.xml
+++ b/app/src/main/res/layout/servo_controller_layout.xml
@@ -49,5 +49,15 @@
android:maxLines="1"
android:imeOptions="flagNoExtractUi"
/>
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/wave_generator_main_controls.xml b/app/src/main/res/layout/wave_generator_main_controls.xml
index ee9278577..87d38bd04 100644
--- a/app/src/main/res/layout/wave_generator_main_controls.xml
+++ b/app/src/main/res/layout/wave_generator_main_controls.xml
@@ -13,6 +13,7 @@
android:layout_marginBottom="@dimen/margin_btn"
android:layout_weight="1"
android:background="@drawable/btn_back_rounded"
+ android:stateListAnimator="@animator/selector_animator"
android:minWidth="@dimen/btn_min_width"
android:text="@string/produce_sound_text"
android:textAllCaps="false"
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index c2723e602..1a2ff27f7 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -80,6 +80,7 @@
80dp
80dp
20sp
+ 9dp
20dp
1dp
@@ -356,6 +357,10 @@
20dp
22dp
12dp
+ 280dp
+ 0dp
+ 4dp
+ 5dp
5dp
40dp
180dp
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 86995d13b..59d3e16ea 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -660,7 +660,7 @@
Hz
Phase :
Phase
- deg
+ \u00b0
Wave Frequency :
SQR1
SQR2
@@ -677,11 +677,13 @@
PWM
View
Produce Sound
+ Stop Sound
Save
- 45 deg
+ 45\u00b0
50 %
Open instrument
+ Reference
In-Built MIC
PSLab MIC
Sine
@@ -704,6 +706,9 @@
\u2022 The above pin configuration has to be same except for the pin
GND. You can use any of the PSLab device GND pins.\n
+ s
+ \u00B0
+
High Limit
Update Period
Max (Lx)
@@ -882,6 +887,28 @@
Gas Contents (PPM)
PPM
+ Dust Sensor
+ Dust Sensor is used to measure air quality
+ Connect pins according to pin diagram and start measuring. Power up the sensor using VCC pin in PSLab and connect data pin to CH1. Make sure the ground wire is connected to one of the GND pins in PSLab.
+ Dust sensor is used to measure air quality in terms of particles per square meter
+ Particles (PPM)
+ PPM
+ 1000
+ Air Quality
+ setting_dust_update_period
+ setting_dust_high_limit
+ setting_dust_sensor_type
+ 1000 ms
+ Please provide threshold value for dust particle value to be recorded (0.0~5.0)
+
+ - DSM-501A
+ - Other
+
+
+ - 0
+ - 1
+
+
Robotic Arm
A robotic arm is a type of mechanical arm, usually programmable, with similar functions to a human arm.
Your arm\'s job is to move your hand from place to place. Similarly, the robotic arm\'s job is to move an end effector from place to place. You can outfit robotic arms with all sorts of end effectors, which are suited to a particular application. One common end effector is a simplified version of the hand, which can grasp and carry different objects.
@@ -1009,5 +1036,6 @@
OpenStreetMap
Location not specified
0
+ Unknown
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index ccde34c4c..199695588 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -44,6 +44,29 @@
- @color/grey
+
+
+
+
+