diff --git a/app/build.gradle b/app/build.gradle index 090d5b33d..7b2b53ae9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,8 +8,8 @@ android { applicationId "io.pslab" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 13 - versionName "2.0.12" + versionCode 14 + versionName "2.0.13" multiDexEnabled true testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } @@ -66,6 +66,9 @@ dependencies { implementation "org.apache.commons:commons-lang3:$rootProject.commonLangVersion" implementation "com.squareup.picasso:picasso:$rootProject.picassoVersion" + implementation 'com.github.GoodieBag:ProtractorView:v1.2' + implementation 'com.github.Triggertrap:SeekArc:v1.1' + implementation "com.jakewharton:butterknife:$rootProject.butterKnifeVersion" implementation 'com.android.support.constraint:constraint-layout:1.1.3' annotationProcessor "com.jakewharton:butterknife-compiler:$rootProject.butterKnifeVersion" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 306a0856d..913bca735 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ @@ -14,6 +15,7 @@ + - + - diff --git a/app/src/main/java/io/pslab/activity/DataLoggerActivity.java b/app/src/main/java/io/pslab/activity/DataLoggerActivity.java index 587a3ae2b..978eb8700 100644 --- a/app/src/main/java/io/pslab/activity/DataLoggerActivity.java +++ b/app/src/main/java/io/pslab/activity/DataLoggerActivity.java @@ -34,6 +34,7 @@ import butterknife.ButterKnife; import io.pslab.R; import io.pslab.adapters.SensorLoggerListAdapter; +import io.pslab.models.AccelerometerData; import io.pslab.models.BaroData; import io.pslab.models.CompassData; import io.pslab.models.GyroData; @@ -321,7 +322,7 @@ private void getFileData(File file) { String[] data = line.split(","); try { time += 1000; - CompassData compassData = new CompassData(time, block, data[2].equals("null")?"0":data[2], data[3].equals("null")?"0":data[3], data[4].equals("null")?"0":data[4], data[5], Double.valueOf(data[6]), Double.valueOf(data[7])); + CompassData compassData = new CompassData(time, block, data[2].equals("null") ? "0" : data[2], data[3].equals("null") ? "0" : data[3], data[4].equals("null") ? "0" : data[4], data[5], Double.valueOf(data[6]), Double.valueOf(data[7])); realm.beginTransaction(); realm.copyToRealm(compassData); realm.commitTransaction(); @@ -377,6 +378,40 @@ private void getFileData(File file) { } catch (IOException e) { e.printStackTrace(); } + } else if (selectedDevice != null && selectedDevice.equals(getResources().getString(R.string.accelerometer))) { + try { + FileInputStream is = new FileInputStream(file); + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + String line = reader.readLine(); + int i = 0; + long block = 0, time = 0; + while (line != null) { + if (i != 0) { + String[] data = line.split(","); + try { + time += 1000; + AccelerometerData accelerometerData = new AccelerometerData(time, block, Float.valueOf(data[2]), Float.valueOf(data[3]), Float.valueOf(data[4]), Double.valueOf(data[5]), Double.valueOf(data[6])); + realm.beginTransaction(); + realm.copyToRealm(accelerometerData); + realm.commitTransaction(); + } catch (Exception e) { + Toast.makeText(this, getResources().getString(R.string.incorrect_import_format), Toast.LENGTH_SHORT).show(); + } + } else { + block = System.currentTimeMillis(); + time = block; + realm.beginTransaction(); + realm.copyToRealm(new SensorDataBlock(block, getResources().getString(R.string.accelerometer))); + realm.commitTransaction(); + } + i++; + line = reader.readLine(); + } + fillData(); + DataLoggerActivity.this.toolbar.getMenu().findItem(R.id.delete_all).setVisible(true); + } catch (IOException e) { + e.printStackTrace(); + } } } } diff --git a/app/src/main/java/io/pslab/activity/RoboticArmActivity.java b/app/src/main/java/io/pslab/activity/RoboticArmActivity.java new file mode 100644 index 000000000..484d218b5 --- /dev/null +++ b/app/src/main/java/io/pslab/activity/RoboticArmActivity.java @@ -0,0 +1,367 @@ +package io.pslab.activity; + +import android.graphics.Point; +import android.os.Build; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.view.Display; +import android.view.DragEvent; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.WindowManager; +import android.view.inputmethod.EditorInfo; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.triggertrap.seekarc.SeekArc; + +import io.pslab.R; + +public class RoboticArmActivity extends AppCompatActivity { + + private EditText degreeText1, degreeText2, degreeText3, degreeText4; + private SeekArc seekArc1, seekArc2, seekArc3, seekArc4; + private LinearLayout servo1TimeLine, servo2TimeLine, servo3TimeLine, servo4TimeLine; + private int degree; + private boolean editEnter = false; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_robotic_arm); + + Display display = getWindowManager().getDefaultDisplay(); + Point size = new Point(); + display.getSize(size); + int screen_width = size.x; + int screen_height = size.y; + + View servo1Layout = findViewById(R.id.servo_1); + View servo2Layout = findViewById(R.id.servo_2); + View servo3Layout = findViewById(R.id.servo_3); + View servo4Layout = findViewById(R.id.servo_4); + degreeText1 = servo1Layout.findViewById(R.id.degreeText); + degreeText2 = servo2Layout.findViewById(R.id.degreeText); + degreeText3 = servo3Layout.findViewById(R.id.degreeText); + degreeText4 = servo4Layout.findViewById(R.id.degreeText); + seekArc1 = servo1Layout.findViewById(R.id.seek_arc); + seekArc2 = servo2Layout.findViewById(R.id.seek_arc); + seekArc3 = servo3Layout.findViewById(R.id.seek_arc); + seekArc4 = servo4Layout.findViewById(R.id.seek_arc); + servo1TimeLine = findViewById(R.id.servo1_timeline); + servo2TimeLine = findViewById(R.id.servo2_timeline); + servo3TimeLine = findViewById(R.id.servo3_timeline); + servo4TimeLine = findViewById(R.id.servo4_timeline); + LinearLayout timeLineControlsLayout = findViewById(R.id.servo_timeline_controls); + + LinearLayout.LayoutParams servoControllerParams = new LinearLayout.LayoutParams(screen_width / 4 - 4, screen_height / 2 - 4); + servoControllerParams.setMargins(2, 5, 2, 0); + servo1Layout.setLayoutParams(servoControllerParams); + servo2Layout.setLayoutParams(servoControllerParams); + servo3Layout.setLayoutParams(servoControllerParams); + servo4Layout.setLayoutParams(servoControllerParams); + + LinearLayout.LayoutParams servoTimeLineParams = new LinearLayout.LayoutParams(screen_width - 4 - screen_width / 15, screen_height / 8 - 2); + servoTimeLineParams.setMargins(2, 2, 2, 0); + + servo1TimeLine.setLayoutParams(servoTimeLineParams); + servo2TimeLine.setLayoutParams(servoTimeLineParams); + servo3TimeLine.setLayoutParams(servoTimeLineParams); + servo4TimeLine.setLayoutParams(servoTimeLineParams); + + LinearLayout.LayoutParams timeLineControlsParams = new LinearLayout.LayoutParams(screen_width / 15, screen_height / 2); + timeLineControlsLayout.setLayoutParams(timeLineControlsParams); + + TextView servo1Title = servo1Layout.findViewById(R.id.servo_title); + servo1Title.setText(getResources().getString(R.string.servo1_title)); + + TextView servo2Title = servo2Layout.findViewById(R.id.servo_title); + servo2Title.setText(getResources().getString(R.string.servo2_title)); + + TextView servo3Title = servo3Layout.findViewById(R.id.servo_title); + servo3Title.setText(getResources().getString(R.string.servo3_title)); + + TextView servo4Title = servo4Layout.findViewById(R.id.servo_title); + servo4Title.setText(getResources().getString(R.string.servo4_title)); + + removeStatusBar(); + + seekArc1.setOnSeekArcChangeListener(new SeekArc.OnSeekArcChangeListener() { + @Override + public void onProgressChanged(SeekArc seekArc, int i, boolean b) { + if (editEnter) { + degreeText1.setText(String.valueOf(degree)); + editEnter = false; + } else { + degreeText1.setText(String.valueOf((int) (i * 3.6))); + } + } + + @Override + public void onStartTrackingTouch(SeekArc seekArc) { + + } + + @Override + public void onStopTrackingTouch(SeekArc seekArc) { + + } + }); + + seekArc2.setOnSeekArcChangeListener(new SeekArc.OnSeekArcChangeListener() { + @Override + public void onProgressChanged(SeekArc seekArc, int i, boolean b) { + if (editEnter) { + degreeText2.setText(String.valueOf(degree)); + editEnter = false; + } else { + degreeText2.setText(String.valueOf((int) (i * 3.6))); + } + } + + @Override + public void onStartTrackingTouch(SeekArc seekArc) { + + } + + @Override + public void onStopTrackingTouch(SeekArc seekArc) { + + } + }); + + seekArc3.setOnSeekArcChangeListener(new SeekArc.OnSeekArcChangeListener() { + @Override + public void onProgressChanged(SeekArc seekArc, int i, boolean b) { + if (editEnter) { + degreeText3.setText(String.valueOf(degree)); + editEnter = false; + } else { + degreeText3.setText(String.valueOf((int) (i * 3.6))); + } + } + + @Override + public void onStartTrackingTouch(SeekArc seekArc) { + + } + + @Override + public void onStopTrackingTouch(SeekArc seekArc) { + + } + }); + + seekArc4.setOnSeekArcChangeListener(new SeekArc.OnSeekArcChangeListener() { + @Override + public void onProgressChanged(SeekArc seekArc, int i, boolean b) { + if (editEnter) { + degreeText4.setText(String.valueOf(degree)); + editEnter = false; + } else { + degreeText4.setText(String.valueOf((int) (i * 3.6))); + } + } + + @Override + public void onStartTrackingTouch(SeekArc seekArc) { + + } + + @Override + public void onStopTrackingTouch(SeekArc seekArc) { + + } + }); + + servo1Layout.findViewById(R.id.drag_handle).setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + View.DragShadowBuilder myShadow = new View.DragShadowBuilder(servo1Layout); + v.startDrag(null, myShadow, servo1Layout, 0); + return true; + } + }); + + servo1TimeLine.setOnDragListener(new View.OnDragListener() { + @Override + public boolean onDrag(View v, DragEvent event) { + if (event.getAction() == DragEvent.ACTION_DRAG_ENTERED) { + View view = (View) event.getLocalState(); + TextView text = view.findViewById(R.id.degreeText); + TextView new_text = (TextView) LayoutInflater.from(RoboticArmActivity.this).inflate(R.layout.robotic_arm_timeline_textview, null); + LinearLayout.LayoutParams timeLineTextParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); + timeLineTextParams.setMargins(5, 1, 1, 1); + new_text.setLayoutParams(timeLineTextParams); + new_text.setText(text.getText()); + if (view.getId() == R.id.servo_1) { + servo1TimeLine.addView(new_text, servo1TimeLine.getChildCount()); + } + } + return true; + } + }); + + servo2Layout.findViewById(R.id.drag_handle).setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + View.DragShadowBuilder myShadow = new View.DragShadowBuilder(servo2Layout); + v.startDrag(null, myShadow, servo2Layout, 0); + return true; + } + }); + + servo2TimeLine.setOnDragListener(new View.OnDragListener() { + @Override + public boolean onDrag(View v, DragEvent event) { + if (event.getAction() == DragEvent.ACTION_DRAG_ENTERED) { + View view = (View) event.getLocalState(); + TextView text = view.findViewById(R.id.degreeText); + TextView new_text = (TextView) LayoutInflater.from(RoboticArmActivity.this).inflate(R.layout.robotic_arm_timeline_textview, null); + LinearLayout.LayoutParams timeLineTextParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); + timeLineTextParams.setMargins(5, 1, 1, 1); + new_text.setLayoutParams(timeLineTextParams); + new_text.setText(text.getText()); + if (view.getId() == R.id.servo_2) { + servo2TimeLine.addView(new_text, servo2TimeLine.getChildCount()); + } + } + return true; + } + }); + + servo3Layout.findViewById(R.id.drag_handle).setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + View.DragShadowBuilder myShadow = new View.DragShadowBuilder(servo3Layout); + v.startDrag(null, myShadow, servo3Layout, 0); + return true; + } + }); + + servo3TimeLine.setOnDragListener(new View.OnDragListener() { + @Override + public boolean onDrag(View v, DragEvent event) { + if (event.getAction() == DragEvent.ACTION_DRAG_ENTERED) { + View view = (View) event.getLocalState(); + TextView text = view.findViewById(R.id.degreeText); + TextView new_text = (TextView) LayoutInflater.from(RoboticArmActivity.this).inflate(R.layout.robotic_arm_timeline_textview, null); + LinearLayout.LayoutParams timeLineTextParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); + timeLineTextParams.setMargins(5, 1, 1, 1); + new_text.setLayoutParams(timeLineTextParams); + new_text.setText(text.getText()); + if (view.getId() == R.id.servo_3) { + servo3TimeLine.addView(new_text, servo3TimeLine.getChildCount()); + } + } + return true; + } + }); + + servo4Layout.findViewById(R.id.drag_handle).setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + View.DragShadowBuilder myShadow = new View.DragShadowBuilder(servo4Layout); + v.startDrag(null, myShadow, servo4Layout, 0); + return true; + } + }); + + servo4TimeLine.setOnDragListener(new View.OnDragListener() { + @Override + public boolean onDrag(View v, DragEvent event) { + if (event.getAction() == DragEvent.ACTION_DRAG_ENTERED) { + View view = (View) event.getLocalState(); + TextView text = view.findViewById(R.id.degreeText); + TextView new_text = (TextView) LayoutInflater.from(RoboticArmActivity.this).inflate(R.layout.robotic_arm_timeline_textview, null); + LinearLayout.LayoutParams timeLineTextParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); + timeLineTextParams.setMargins(5, 1, 1, 1); + new_text.setLayoutParams(timeLineTextParams); + new_text.setText(text.getText()); + if (view.getId() == R.id.servo_4) { + servo4TimeLine.addView(new_text, servo4TimeLine.getChildCount()); + } + } + return true; + } + }); + + degreeText1.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + removeStatusBar(); + if (actionId == EditorInfo.IME_ACTION_DONE) { + degree = Integer.valueOf(degreeText1.getText().toString()); + seekArc1.setProgress((int) (degree / 3.6)); + editEnter = true; + } + return false; + } + }); + + degreeText2.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + removeStatusBar(); + if (actionId == EditorInfo.IME_ACTION_DONE) { + degree = Integer.valueOf(degreeText2.getText().toString()); + seekArc2.setProgress((int) (degree / 3.6)); + editEnter = true; + } + return false; + } + }); + + degreeText3.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + removeStatusBar(); + if (actionId == EditorInfo.IME_ACTION_DONE) { + degree = Integer.valueOf(degreeText3.getText().toString()); + seekArc3.setProgress((int) (degree / 3.6)); + editEnter = true; + } + return false; + } + }); + + degreeText4.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + removeStatusBar(); + if (actionId == EditorInfo.IME_ACTION_DONE) { + degree = Integer.valueOf(degreeText4.getText().toString()); + seekArc4.setProgress((int) (degree / 3.6)); + editEnter = true; + } + return false; + } + }); + + } + + @Override + protected void onResume() { + super.onResume(); + removeStatusBar(); + } + + private void removeStatusBar() { + if (Build.VERSION.SDK_INT < 16) { + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + } else { + View decorView = getWindow().getDecorView(); + + decorView.setSystemUiVisibility((View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)); + } + } +} diff --git a/app/src/main/java/io/pslab/activity/SettingsActivity.java b/app/src/main/java/io/pslab/activity/SettingsActivity.java index dd3f8c5a4..a051140b3 100644 --- a/app/src/main/java/io/pslab/activity/SettingsActivity.java +++ b/app/src/main/java/io/pslab/activity/SettingsActivity.java @@ -65,7 +65,7 @@ protected void onCreate(Bundle savedInstanceState) { case PSLabSensor.GYROSCOPE_CONFIGURATIONS: fragment = new GyroscopeSettingsFragment(); break; - case PSLabSensor.ACCELEROMETER: + case PSLabSensor.ACCELEROMETER_CONFIGURATIONS: fragment = new AccelerometerSettingsFragment(); break; default: diff --git a/app/src/main/java/io/pslab/adapters/SensorLoggerListAdapter.java b/app/src/main/java/io/pslab/adapters/SensorLoggerListAdapter.java index 4ae7af14f..c3bb13a38 100644 --- a/app/src/main/java/io/pslab/adapters/SensorLoggerListAdapter.java +++ b/app/src/main/java/io/pslab/adapters/SensorLoggerListAdapter.java @@ -85,7 +85,7 @@ public void onBindViewHolder(@NonNull final ViewHolder holder, int position) { holder.sensor.setText(context.getResources().getString(R.string.compass)); holder.tileIcon.setImageDrawable(context.getResources().getDrawable(R.drawable.tile_icon_compass_log)); break; - case PSLabSensor.ACCELEROMETER: + case PSLabSensor.ACCELEROMETER_CONFIGURATIONS: holder.sensor.setText(context.getResources().getString(R.string.accelerometer)); holder.tileIcon.setImageDrawable(context.getResources().getDrawable(R.drawable.tile_icon_accelerometer)); break; @@ -166,7 +166,7 @@ public void onClick(DialogInterface dialog, int whichButton) { LocalDataLog.with().clearBlockOfBaroRecords(block.getBlock()); } else if (block.getSensorType().equalsIgnoreCase(PSLabSensor.COMPASS)) { LocalDataLog.with().clearBlockOfCompassRecords(block.getBlock()); - } else if (block.getSensorType().equalsIgnoreCase(PSLabSensor.ACCELEROMETER)) { + } else if (block.getSensorType().equalsIgnoreCase(PSLabSensor.ACCELEROMETER_CONFIGURATIONS)) { LocalDataLog.with().clearBlockOfAccelerometerRecords(block.getBlock()); } LocalDataLog.with().clearSensorBlock(block.getBlock()); @@ -256,7 +256,7 @@ private void populateMapData(SensorDataBlock block) { } } setMapDataToIntent(array); - } else if (block.getSensorType().equalsIgnoreCase(PSLabSensor.ACCELEROMETER)) { + } else if (block.getSensorType().equalsIgnoreCase(PSLabSensor.ACCELEROMETER_CONFIGURATIONS)) { RealmResults data = LocalDataLog.with().getBlockOfAccelerometerRecords(block.getBlock()); JSONArray array = new JSONArray(); for (AccelerometerData d : data) { diff --git a/app/src/main/java/io/pslab/fragment/AccelerometerDataFragment.java b/app/src/main/java/io/pslab/fragment/AccelerometerDataFragment.java index 8e440ac18..c808e3eef 100644 --- a/app/src/main/java/io/pslab/fragment/AccelerometerDataFragment.java +++ b/app/src/main/java/io/pslab/fragment/AccelerometerDataFragment.java @@ -1,5 +1,6 @@ package io.pslab.fragment; +import android.graphics.Bitmap; import android.graphics.Color; import android.hardware.Sensor; import android.hardware.SensorEvent; @@ -7,6 +8,7 @@ import android.hardware.SensorManager; import android.location.Location; import android.os.Bundle; +import android.os.Environment; import android.os.Handler; import android.support.annotation.NonNull; import android.support.v4.app.Fragment; @@ -26,10 +28,18 @@ import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileWriter; import java.io.IOException; +import java.io.PrintWriter; import java.text.DecimalFormat; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; +import java.util.Locale; import java.util.Timer; import java.util.TimerTask; @@ -48,6 +58,7 @@ import io.pslab.others.ScienceLabCommon; import static android.content.Context.SENSOR_SERVICE; +import static io.pslab.others.CSVLogger.CSV_DIRECTORY; /** * Created by Kunal on 18-12-18 @@ -118,7 +129,7 @@ private enum ACCELEROMETER_SENSOR {INBUILT_SENSOR, BH1750_SENSOR, TSL2561_SENSOR private SensorManager sensorManager; private Sensor sensor; private long startTime, block; - private ArrayList entriesX,entriesY,entriesZ; + private ArrayList entriesX, entriesY, entriesZ; private ArrayList recordedAccelerometerArray; private AccelerometerData sensorData; private float currentMinX = Integer.MAX_VALUE; @@ -134,6 +145,7 @@ private enum ACCELEROMETER_SENSOR {INBUILT_SENSOR, BH1750_SENSOR, TSL2561_SENSOR private long previousTimeElapsed_Z = (System.currentTimeMillis() - startTime) / updatePeriod; private AccelerometerActivity accelerometerSensor; private DecimalFormat df = new DecimalFormat("+#0.0;-#0.0"); + private View rootView; public static AccelerometerDataFragment newInstance() { return new AccelerometerDataFragment(); @@ -159,10 +171,10 @@ public void onCreate(Bundle savedInstanceState) { @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_accelerometer_data, container, false); - unbinder = ButterKnife.bind(this, view); + rootView = inflater.inflate(R.layout.fragment_accelerometer_data, container, false); + unbinder = ButterKnife.bind(this, rootView); setupInstruments(); - return view; + return rootView; } @Override @@ -172,7 +184,7 @@ public void onResume() { recordedAccelerometerArray = new ArrayList<>(); resetInstrumentData(); playRecordedData(); - }else if (accelerometerSensor.viewingData) { + } else if (accelerometerSensor.viewingData) { recordedAccelerometerArray = new ArrayList<>(); resetInstrumentData(); plotAllRecordedData(); @@ -208,7 +220,7 @@ public void onResume() { private void plotAllRecordedData() { recordedAccelerometerArray.addAll(accelerometerSensor.recordedAccelerometerData); if (recordedAccelerometerArray.size() != 0) { - for (AccelerometerData d: recordedAccelerometerArray) { + for (AccelerometerData d : recordedAccelerometerArray) { if (currentMaxX < d.getAccelerometerX()) { currentMaxX = d.getAccelerometerX(); } @@ -481,7 +493,40 @@ public void stopData() { } public void saveGraph() { - // Todo: Save graph view to gallery + String fileName = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.getDefault()).format(accelerometerSensor.recordedAccelerometerData.get(0).getTime()); + File csvFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + + File.separator + CSV_DIRECTORY + File.separator + accelerometerSensor.getSensorName() + + File.separator + fileName + ".csv"); + if (!csvFile.exists()) { + try { + csvFile.createNewFile(); + PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(csvFile, true))); + out.write("Timestamp,DateTime,ReadingsX,ReadingsY,ReadingsZ,Latitude,Longitude\n"); + for (AccelerometerData accelerometerData : accelerometerSensor.recordedAccelerometerData) { + out.write(accelerometerData.getTime() + "," + + CSVLogger.FILE_NAME_FORMAT.format(new Date(accelerometerData.getTime())) + "," + + accelerometerData.getAccelerometerX() + "," + + accelerometerData.getAccelerometerY() + "," + + accelerometerData.getAccelerometerZ() + "," + + accelerometerData.getLat() + "," + + accelerometerData.getLon() + "," + "\n"); + } + out.flush(); + out.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + View view = rootView.findViewById(R.id.accelerometer_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 + accelerometerSensor.getSensorName() + + File.separator + CSVLogger.FILE_NAME_FORMAT.format(new Date()) + "_graph.jpg")); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } } @@ -656,12 +701,12 @@ private void writeLogToFile(long timestamp, float readingX, float readingY, floa Location location = accelerometerSensor.gpsLogger.getDeviceLocation(); accelerometerSensor.csvLogger.writeCSVFile(timestamp + "," + dateTime + "," + readingX + "," + readingY + "," + readingZ + "," + location.getLatitude() + "," + location.getLongitude()); - sensorData = new AccelerometerData(timestamp, block, accelerometerValue_X,accelerometerValue_Y,accelerometerValue_Z, location.getLatitude(), location.getLongitude()); + sensorData = new AccelerometerData(timestamp, block, accelerometerValue_X, accelerometerValue_Y, accelerometerValue_Z, location.getLatitude(), location.getLongitude()); } else { String dateTime = CSVLogger.FILE_NAME_FORMAT.format(new Date(timestamp)); accelerometerSensor.csvLogger.writeCSVFile(timestamp + "," + dateTime + "," + readingX + "," + readingY + "," + readingZ + ",0.0,0.0"); - sensorData = new AccelerometerData(timestamp, block, accelerometerValue_X,accelerometerValue_Y,accelerometerValue_Z, 0.0, 0.0); + sensorData = new AccelerometerData(timestamp, block, accelerometerValue_X, accelerometerValue_Y, accelerometerValue_Z, 0.0, 0.0); } accelerometerSensor.recordSensorData(sensorData); } else { @@ -805,7 +850,7 @@ public void onSensorChanged(SensorEvent event) { } }; - private void resetInstrumentData(){ + private void resetInstrumentData() { accelerometerValue_X = 0; accelerometerValue_Y = 0; accelerometerValue_Z = 0; @@ -833,20 +878,17 @@ private void initiateAccelerometerSensor(int type) { ScienceLab scienceLab; switch (s) { case INBUILT_SENSOR: - //z_accel_max_text.setText(getResources().getStringArray(R.array.lux_sensors)[0]); sensorManager = (SensorManager) getContext().getSystemService(SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); if (sensor == null) { Toast.makeText(getContext(), getResources().getString(R.string.no_accelerometer_sensor), Toast.LENGTH_LONG).show(); } else { float max = sensor.getMaximumRange(); - //z_tv_label_left_yaxis_hmc.setMaxSpeed(max); sensorManager.registerListener(accelerometerSensorEventListener, sensor, SensorManager.SENSOR_DELAY_FASTEST); } break; case BH1750_SENSOR: - //z_accel_max_text.setText(getResources().getStringArray(R.array.lux_sensors)[1]); scienceLab = ScienceLabCommon.scienceLab; if (scienceLab.isConnected()) { ArrayList data; diff --git a/app/src/main/java/io/pslab/fragment/AccelerometerSettingsFragment.java b/app/src/main/java/io/pslab/fragment/AccelerometerSettingsFragment.java index 27f9cc5f4..a02d6171f 100644 --- a/app/src/main/java/io/pslab/fragment/AccelerometerSettingsFragment.java +++ b/app/src/main/java/io/pslab/fragment/AccelerometerSettingsFragment.java @@ -18,10 +18,10 @@ public class AccelerometerSettingsFragment extends PreferenceFragmentCompat implements SharedPreferences.OnSharedPreferenceChangeListener { public static final String KEY_INCLUDE_LOCATION = "include_location_sensor_data"; - public static final String KEY_UPDATE_PERIOD = "setting_lux_update_period"; - public static final String KEY_HIGH_LIMIT = "setting_lux_high_limit"; - public static final String KEY_ACCELEROMETER_SENSOR_TYPE = "setting_lux_sensor_type"; - public static final String KEY_ACCELEROMETER_SENSOR_GAIN = "setting_lux_sensor_gain"; + public static final String KEY_UPDATE_PERIOD = "setting_accelerometer_update_period"; + public static final String KEY_HIGH_LIMIT = "setting_accelerometer_high_limit"; + public static final String KEY_ACCELEROMETER_SENSOR_TYPE = "setting_accelerometer_sensor_type"; + public static final String KEY_ACCELEROMETER_SENSOR_GAIN = "setting_accelerometer_sensor_gain"; private PSLabPermission psLabPermission; @@ -34,7 +34,7 @@ public class AccelerometerSettingsFragment extends PreferenceFragmentCompat impl @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { - setPreferencesFromResource(R.xml.lux_meter_settings, rootKey); + setPreferencesFromResource(R.xml.accelerometer_settings, rootKey); updatePeriodPref = (EditTextPreference) getPreferenceScreen().findPreference(KEY_UPDATE_PERIOD); higLimitPref = (EditTextPreference) getPreferenceScreen().findPreference(KEY_HIGH_LIMIT); sensorGainPref = (EditTextPreference) getPreferenceScreen().findPreference(KEY_ACCELEROMETER_SENSOR_GAIN); @@ -55,7 +55,7 @@ public void onResume() { super.onResume(); locationPreference.setChecked(sharedPref.getBoolean(KEY_INCLUDE_LOCATION, true)); updatePeriodPref.setSummary(updatePeriodPref.getText() + " ms"); - higLimitPref.setSummary(higLimitPref.getText() + " Lx"); + higLimitPref.setSummary(higLimitPref.getText() + " m/s²"); sensorTypePreference.setSummary(sensorTypePreference.getEntry()); sensorGainPref.setSummary(sensorGainPref.getText()); getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this); diff --git a/app/src/main/java/io/pslab/fragment/CompassDataFragment.java b/app/src/main/java/io/pslab/fragment/CompassDataFragment.java index 836d1761b..1fe1ff32d 100644 --- a/app/src/main/java/io/pslab/fragment/CompassDataFragment.java +++ b/app/src/main/java/io/pslab/fragment/CompassDataFragment.java @@ -1,11 +1,13 @@ package io.pslab.fragment; +import android.graphics.Bitmap; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; 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; @@ -21,8 +23,17 @@ import android.widget.Toast; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; +import java.util.Locale; import java.util.Timer; import java.util.TimerTask; @@ -36,8 +47,9 @@ import io.pslab.others.CSVLogger; import static android.content.Context.SENSOR_SERVICE; +import static io.pslab.others.CSVLogger.CSV_DIRECTORY; -public class CompassDataFragment extends Fragment{ +public class CompassDataFragment extends Fragment { private Unbinder unbinder; @Nullable @@ -73,6 +85,8 @@ public class CompassDataFragment extends Fragment{ private long previousTimeElapsed = (System.currentTimeMillis() - startTime) / updatePeriod; private int turns = 0; private boolean returningFromPause = false; + private View rootView; + public static CompassDataFragment newInstance() { return new CompassDataFragment(); } @@ -86,8 +100,8 @@ public void onCreate(@Nullable Bundle savedInstanceState) { @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.activity_compass, container, false); - unbinder = ButterKnife.bind(this, view); + rootView = inflater.inflate(R.layout.activity_compass, container, false); + unbinder = ButterKnife.bind(this, rootView); xAxisRadioButton.setChecked(true); direction = 0; @@ -125,7 +139,7 @@ public void onClick(View v) { } }); compassActivity.addLocation = true; - return view; + return rootView; } private SensorEventListener compassEventListner = new SensorEventListener() { @@ -218,7 +232,41 @@ public void onResume() { } public void saveGraph() { - // Todo: Save graph view to gallery + String fileName = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.getDefault()).format(compassActivity.recordedCompassData.get(0).getTime()); + File csvFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + + File.separator + CSV_DIRECTORY + File.separator + compassActivity.getSensorName() + + File.separator + fileName + ".csv"); + if (!csvFile.exists()) { + try { + csvFile.createNewFile(); + PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(csvFile, true))); + out.write("Timestamp,DateTime,X-reading,Y-reading,Z-reading,Axis,Latitude,Longitude\n"); + for (CompassData compassData : compassActivity.recordedCompassData) { + out.write(compassData.getTime() + "," + + CSVLogger.FILE_NAME_FORMAT.format(new Date(compassData.getTime())) + "," + + compassData.getBx() + "," + + compassData.getBy() + "," + + compassData.getBz() + "," + + compassData.getAxis() + "," + + compassData.getLat() + "," + + compassData.getLon() + "," + "\n"); + } + out.flush(); + out.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + View view = rootView.findViewById(R.id.compass_card); + 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 + compassActivity.getSensorName() + + File.separator + CSVLogger.FILE_NAME_FORMAT.format(new Date()) + "_graph.jpg")); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } } private void processRecordedData(long timeGap) { @@ -313,8 +361,6 @@ public void run() { compassData.setBz(String.valueOf(degree)); zAxisMagneticField.setText(String.valueOf(degree)); - - } catch (IndexOutOfBoundsException e) { graphTimer.cancel(); graphTimer = null; @@ -423,15 +469,15 @@ private void plotAllRecordedData() { compassData.setBz(String.valueOf(degree)); zAxisMagneticField.setText(String.valueOf(degree)); - if (d.getAxis().equals(getContext().getResources().getString(R.string.compass_X_axis))){ + if (d.getAxis().equals(getContext().getResources().getString(R.string.compass_X_axis))) { xAxisRadioButton.setChecked(true); yAxisRadioButton.setChecked(false); zAxisRadioButton.setChecked(false); - } else if (d.getAxis().equals(getContext().getResources().getString(R.string.compass_Y_axis))){ + } else if (d.getAxis().equals(getContext().getResources().getString(R.string.compass_Y_axis))) { xAxisRadioButton.setChecked(false); yAxisRadioButton.setChecked(true); zAxisRadioButton.setChecked(false); - } else if (d.getAxis().equals(getContext().getResources().getString(R.string.compass_Z_axis))){ + } else if (d.getAxis().equals(getContext().getResources().getString(R.string.compass_Z_axis))) { xAxisRadioButton.setChecked(false); yAxisRadioButton.setChecked(false); zAxisRadioButton.setChecked(true); @@ -467,6 +513,7 @@ public void onDestroyView() { } unbinder.unbind(); } + @Override public void onPause() { super.onPause(); @@ -525,12 +572,12 @@ private void writeLogToFile(long timestamp, String compassXvalue, String compass String dateTime = CSVLogger.FILE_NAME_FORMAT.format(new Date(timestamp)); Location location = compassActivity.gpsLogger.getDeviceLocation(); compassActivity.csvLogger.writeCSVFile(timestamp + "," + dateTime + "," - + compassXvalue + "," + compassYvalue + "," + compassZvalue + "," + compassAxis + "," + location.getLatitude() + "," + location.getLongitude()); + + compassXvalue + "," + compassYvalue + "," + compassZvalue + "," + compassAxis + "," + location.getLatitude() + "," + location.getLongitude()); compassData = new CompassData(timestamp, block, compassXvalue, compassYvalue, compassZvalue, compassAxis, location.getLatitude(), location.getLongitude()); } else { String dateTime = CSVLogger.FILE_NAME_FORMAT.format(new Date(timestamp)); compassActivity.csvLogger.writeCSVFile(timestamp + "," + dateTime + "," - + compassXvalue + "," + compassYvalue + "," + compassZvalue + "," + compassAxis + ",0.0, 0.0" ); + + compassXvalue + "," + compassYvalue + "," + compassZvalue + "," + compassAxis + ",0.0, 0.0"); compassData = new CompassData(timestamp, block, compassXvalue, compassYvalue, compassZvalue, compassAxis, 0.0, 0.0); } compassActivity.recordSensorData(compassData); @@ -555,8 +602,7 @@ private void initiateCompassSensor() { sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION); if (sensor == null) { Toast.makeText(getContext(), getContext().getResources().getString(R.string.no_compass_sensor), Toast.LENGTH_SHORT).show(); - } - else { + } else { sensorManager.registerListener(compassEventListner, sensor, SensorManager.SENSOR_DELAY_GAME); } } diff --git a/app/src/main/java/io/pslab/fragment/GyroscopeDataFragment.java b/app/src/main/java/io/pslab/fragment/GyroscopeDataFragment.java index a8a879144..a3ca7a926 100644 --- a/app/src/main/java/io/pslab/fragment/GyroscopeDataFragment.java +++ b/app/src/main/java/io/pslab/fragment/GyroscopeDataFragment.java @@ -1,5 +1,6 @@ package io.pslab.fragment; +import android.graphics.Bitmap; import android.graphics.Color; import android.hardware.Sensor; import android.hardware.SensorEvent; @@ -7,6 +8,7 @@ import android.hardware.SensorManager; import android.location.Location; import android.os.Bundle; +import android.os.Environment; import android.os.Handler; import android.support.annotation.NonNull; import android.support.v4.app.Fragment; @@ -21,9 +23,18 @@ import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; import java.text.DecimalFormat; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; +import java.util.Locale; import java.util.Timer; import java.util.TimerTask; @@ -34,6 +45,7 @@ import io.pslab.others.CSVLogger; import static android.content.Context.SENSOR_SERVICE; +import static io.pslab.others.CSVLogger.CSV_DIRECTORY; public class GyroscopeDataFragment extends Fragment { @@ -52,6 +64,7 @@ public class GyroscopeDataFragment extends Fragment { private ArrayList gyroscopeViewFragments = new ArrayList<>(); private int[] colors = {Color.YELLOW, Color.MAGENTA, Color.GREEN}; private DecimalFormat df = new DecimalFormat("+#0.0;-#0.0"); + private View rootView; public static GyroscopeDataFragment newInstance() { return new GyroscopeDataFragment(); @@ -80,7 +93,7 @@ public void onCreate(Bundle savedInstanceState) { @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_gyroscope_data, container, false); + rootView = inflater.inflate(R.layout.fragment_gyroscope_data, container, false); gyroscopeViewFragments.clear(); gyroscopeViewFragments.add((GyroscopeViewFragment) getChildFragmentManager().findFragmentById(R.id.gyroscope_x_axis_fragment)); gyroscopeViewFragments.add((GyroscopeViewFragment) getChildFragmentManager().findFragmentById(R.id.gyroscope_y_axis_fragment)); @@ -90,7 +103,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, gyroscopeViewFragments.get(2).getGyroAxisImage().setImageResource(R.drawable.phone_z_axis); setupInstruments(); - return view; + return rootView; } @Override @@ -280,7 +293,40 @@ public void stopData() { } public void saveGraph() { - // Todo: Save graph view to gallery + String fileName = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.getDefault()).format(gyroSensor.recordedGyroData.get(0).getTime()); + File csvFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + + File.separator + CSV_DIRECTORY + File.separator + gyroSensor.getSensorName() + + File.separator + fileName + ".csv"); + if (!csvFile.exists()) { + try { + csvFile.createNewFile(); + PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(csvFile, true))); + out.write("Timestamp,DateTime,ReadingsX,ReadingsY,ReadingsZ,Latitude,Longitude\n"); + for (GyroData gyroData : gyroSensor.recordedGyroData) { + out.write(gyroData.getTime() + "," + + CSVLogger.FILE_NAME_FORMAT.format(new Date(gyroData.getTime())) + "," + + gyroData.getGyroX() + "," + + gyroData.getGyroY() + "," + + gyroData.getGyroZ() + "," + + gyroData.getLat() + "," + + gyroData.getLon() + "," + "\n"); + } + out.flush(); + out.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + View view = rootView.findViewById(R.id.gyro_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 + gyroSensor.getSensorName() + + File.separator + CSVLogger.FILE_NAME_FORMAT.format(new Date()) + "_graph.jpg")); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } } private void setupInstruments() { diff --git a/app/src/main/java/io/pslab/fragment/InstrumentsFragment.java b/app/src/main/java/io/pslab/fragment/InstrumentsFragment.java index 77707aac1..e8f545c0d 100644 --- a/app/src/main/java/io/pslab/fragment/InstrumentsFragment.java +++ b/app/src/main/java/io/pslab/fragment/InstrumentsFragment.java @@ -24,6 +24,7 @@ import io.pslab.activity.MultimeterActivity; import io.pslab.activity.OscilloscopeActivity; import io.pslab.activity.PowerSourceActivity; +import io.pslab.activity.RoboticArmActivity; import io.pslab.activity.SensorActivity; import io.pslab.activity.WaveGeneratorActivity; import io.pslab.adapters.ApplicationAdapter; @@ -106,6 +107,10 @@ public void onItemClick(ApplicationItem item) { intent = new Intent(context, GyroscopeActivity.class); startActivity(intent); break; + case "Robotic Arm": + intent = new Intent(context, RoboticArmActivity.class); + startActivity(intent); + break; default: break; } @@ -149,7 +154,8 @@ protected Void doInBackground(Void... params) { R.string.accelerometer_description, R.string.baro_meter_description, R.string.compass_description, - R.string.gyroscope_description + R.string.gyroscope_description, + R.string.robotic_arm_descriptoin }; applicationItemList.add(new ApplicationItem( @@ -185,6 +191,9 @@ protected Void doInBackground(Void... params) { applicationItemList.add(new ApplicationItem( getResources().getString(R.string.gyroscope), R.drawable.gyroscope_logo, getResources().getString(descriptions[10]) )); + applicationItemList.add(new ApplicationItem( + getResources().getString(R.string.robotic_arm), R.drawable.gyroscope_logo, getResources().getString(descriptions[11]) + )); return null; } diff --git a/app/src/main/java/io/pslab/models/PSLabSensor.java b/app/src/main/java/io/pslab/models/PSLabSensor.java index bdab463f1..18fe6a457 100644 --- a/app/src/main/java/io/pslab/models/PSLabSensor.java +++ b/app/src/main/java/io/pslab/models/PSLabSensor.java @@ -100,7 +100,7 @@ public abstract class PSLabSensor extends AppCompatActivity { public static final String GYROSCOPE_CONFIGURATIONS = "Gyroscope Configurations"; public static final String COMPASS = "Compass"; public static final String COMPASS_CONFIGURATIONS = "Compass Configurations"; - public static final String ACCELEROMETER = "Accelerometer"; + public static final String ACCELEROMETER_CONFIGURATIONS = "Accelerometer Configurations"; public static final String ACCELEROMETER_DATA_FORMAT = "%.2f"; @BindView(R.id.sensor_toolbar) diff --git a/app/src/main/res/drawable/servo_drag_handle.png b/app/src/main/res/drawable/servo_drag_handle.png new file mode 100644 index 000000000..12e2669a7 Binary files /dev/null and b/app/src/main/res/drawable/servo_drag_handle.png differ diff --git a/app/src/main/res/layout/activity_compass.xml b/app/src/main/res/layout/activity_compass.xml index 98f362ebf..006406c54 100644 --- a/app/src/main/res/layout/activity_compass.xml +++ b/app/src/main/res/layout/activity_compass.xml @@ -1,6 +1,5 @@ - @@ -17,11 +16,11 @@ android:id="@+id/compass_card" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginEnd="@dimen/card_margin" - android:layout_marginLeft="@dimen/card_margin" - android:layout_marginRight="@dimen/card_margin" android:layout_marginStart="@dimen/card_margin" - android:layout_marginTop="@dimen/card_margin"> + android:layout_marginLeft="@dimen/card_margin" + android:layout_marginTop="@dimen/card_margin" + android:layout_marginEnd="@dimen/card_margin" + android:layout_marginRight="@dimen/card_margin"> + android:layout_marginLeft="@dimen/card_margin" + android:layout_marginTop="@dimen/card_margin" + android:layout_marginEnd="@dimen/card_margin" + android:layout_marginRight="@dimen/card_margin"> + android:layout_marginBottom="@dimen/header_padding_top"> + android:layout_marginLeft="@dimen/card_margin" + android:layout_marginTop="@dimen/card_margin" + android:layout_marginBottom="@dimen/card_margin"> diff --git a/app/src/main/res/layout/activity_robotic_arm.xml b/app/src/main/res/layout/activity_robotic_arm.xml new file mode 100644 index 000000000..f9eea5ece --- /dev/null +++ b/app/src/main/res/layout/activity_robotic_arm.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +