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" />