Work on search menu, add "compiled" view mode

This commit is contained in:
Astatin3
2024-07-02 21:48:01 -06:00
parent 3084224af9
commit 793ba1a75d
6 changed files with 240 additions and 34 deletions
+7 -8
View File
@@ -2,19 +2,18 @@
Ridgebotics 2025 scouting app in Android Ridgebotics 2025 scouting app in Android
TODO: TODO:
- Make the "Search" menu
- Add the rest of the "Compiled" mode graphs
- Make pit and match data field builder UIs. I don't want to have to keep editing a variable
- Add more types of data fields.
- Make the "Compile" menu
- Make the file browser UI - Make the file browser UI
- Add white border around the datamatrix code to allow file transfer in dark mode - Add white border around the datamatrix code to allow file transfer in dark mode
- Fix the code scanning progress indicator - Fix the code scanning progress indicator
- Make pit and match data field builder UIs. I don't want to have to keep editing a variable
Also TODO:
- Add more types of data fields.
- Make the "Search" menu
- Make the "Compile" menu
Also Also TODO:
- Make practice mode
- Make server software to allow for easy sync over wifi - Make server software to allow for easy sync over wifi
- Make practice mode
- AI overview of scouting data for a team??? - AI overview of scouting data for a team???
- Bluetooth data sync - Bluetooth data sync
- Test the scouting app - Test the scouting app
+1
View File
@@ -61,4 +61,5 @@ dependencies {
implementation("com.journeyapps:zxing-android-embedded:4.3.0") implementation("com.journeyapps:zxing-android-embedded:4.3.0")
implementation("com.github.skydoves:powerspinner:1.2.7") implementation("com.github.skydoves:powerspinner:1.2.7")
implementation("com.github.PhilJay:MPAndroidChart:v3.1.0")
} }
@@ -26,6 +26,7 @@ public class sv1 extends sv0 {
writeTag("match_num", "0"); writeTag("match_num", "0");
writeTag("alliance_pos", "red-1"); writeTag("alliance_pos", "red-1");
writeTag("compiled_mode", "false");
} }
public int get_match_num(){ public int get_match_num(){
@@ -61,4 +62,12 @@ public class sv1 extends sv0 {
set_team_num(Integer.parseInt(str)); set_team_num(Integer.parseInt(str));
} }
public void set_team_num(int num){writeTag("team_num", String.valueOf(num));} public void set_team_num(int num){writeTag("team_num", String.valueOf(num));}
public boolean get_compiled_mode(){
return readTag("compiled_mode").equals("true");
}
public void set_compiled_mode(boolean state){
writeTag("compiled_mode", state ? "true" : "false");
}
} }
@@ -2,6 +2,7 @@ package com.astatin3.scoutingapp2025.scoutingData;
import android.app.slice.Slice; import android.app.slice.Slice;
import android.content.Context; import android.content.Context;
import android.graphics.Color;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.view.Gravity; import android.view.Gravity;
@@ -18,6 +19,11 @@ import androidx.annotation.Nullable;
import com.astatin3.scoutingapp2025.SettingsVersionStack.latestSettings; import com.astatin3.scoutingapp2025.SettingsVersionStack.latestSettings;
import com.astatin3.scoutingapp2025.utility.BuiltByteParser; import com.astatin3.scoutingapp2025.utility.BuiltByteParser;
import com.astatin3.scoutingapp2025.utility.ByteBuilder; import com.astatin3.scoutingapp2025.utility.ByteBuilder;
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.google.android.material.slider.Slider; import com.google.android.material.slider.Slider;
import com.skydoves.powerspinner.IconSpinnerAdapter; import com.skydoves.powerspinner.IconSpinnerAdapter;
import com.skydoves.powerspinner.IconSpinnerItem; import com.skydoves.powerspinner.IconSpinnerItem;
@@ -74,6 +80,7 @@ ScoutingVersion {
public Object default_value; public Object default_value;
public abstract inputTypes getInputType(); public abstract inputTypes getInputType();
public abstract valueTypes getValueType(); public abstract valueTypes getValueType();
public abstract Object get_fallback_value();
public abstract int get_byte_id(); public abstract int get_byte_id();
public inputType(){} public inputType(){}
public inputType(String name){ public inputType(String name){
@@ -87,6 +94,8 @@ ScoutingVersion {
public abstract void setViewValue(Object value); public abstract void setViewValue(Object value);
public abstract dataType getViewValue(); public abstract dataType getViewValue();
public abstract void add_individual_view(LinearLayout parent, dataType data); public abstract void add_individual_view(LinearLayout parent, dataType data);
public abstract void add_compiled_view(LinearLayout parent, dataType[] data);
} }
@@ -103,6 +112,7 @@ ScoutingVersion {
public int get_byte_id() {return slider_type_id;} public int get_byte_id() {return slider_type_id;}
public inputTypes getInputType(){return inputTypes.SLIDER;} public inputTypes getInputType(){return inputTypes.SLIDER;}
public valueTypes getValueType(){return valueTypes.NUM;} public valueTypes getValueType(){return valueTypes.NUM;}
public Object get_fallback_value(){return 0;}
public sliderType(){}; public sliderType(){};
public sliderType(String name, int defaultValue, int min, int max){ public sliderType(String name, int defaultValue, int min, int max){
super(name); super(name);
@@ -166,6 +176,111 @@ ScoutingVersion {
slider.setEnabled(false); slider.setEnabled(false);
parent.addView(slider); parent.addView(slider);
} }
private float calculateMean(int[] data) {
float sum = 0;
for (int value : data) {
sum += (float) value;
}
return sum / data.length;
}
private float calculateStandardDeviation(int[] data, float mean) {
float sum = 0;
for (int value : data) {
sum += Math.pow((float) value - mean, 2);
}
return (float) Math.sqrt(sum / (data.length - 1));
}
private List<Entry> generateNormalDistribution(float mean, float stdDev, int count, int scale) {
List<Entry> entries = new ArrayList<>();
for (int i = 0; i < count; i++) {
float x = i;
float y = (float) ((1 / (stdDev * Math.sqrt(2 * Math.PI)))
* Math.exp(-0.5 * Math.pow((x - mean) / stdDev, 2)));
entries.add(new Entry(x, y*scale)); // Scale y for visibility
}
return entries;
}
public void add_compiled_view(LinearLayout parent, dataType[] data){
LineChart chart = new LineChart(parent.getContext());
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
layout.height = 350;
chart.setLayoutParams(layout);
chart.setBackgroundColor(0xff252025);
int[] values = new int[max-min];
for (int i = 0; i < data.length; i++)
values[(int) data[i].get()-min-1]++;
int[] temp = new int[data.length];
for (int i = 0; i < data.length; i++)
temp[i] = (int) data[i].get();
List<Entry> entries = new ArrayList<>();
for (int i = 0; i < values.length; i++)
entries.add(new Entry(i, values[i]));
LineDataSet dataSet = new LineDataSet(entries, name);
dataSet.setColor(Color.BLUE);
dataSet.setValueTextColor(Color.BLACK);
dataSet.setDrawCircles(false);
dataSet.setDrawValues(false);
// Calculate mean and standard deviation
float mean = calculateMean(temp);
float stdDev = calculateStandardDeviation(temp, mean);
// Generate normal distribution curve
List<Entry> normalDistEntries = generateNormalDistribution(mean-min, stdDev, max-min, (max-min)/data.length);
LineDataSet normalDistSet = new LineDataSet(normalDistEntries, "Normal Distribution");
normalDistSet.setColor(Color.RED);
normalDistSet.setDrawCircles(false);
normalDistSet.setDrawValues(false);
normalDistSet.setLineWidth(2f);
LineData lineData = new LineData(dataSet, normalDistSet);
chart.setData(lineData);
chart.invalidate();
chart.getDescription().setEnabled(false);
chart.setTouchEnabled(false);
chart.setDragEnabled(false);
chart.setScaleEnabled(false);
dataSet.setValueTextColor(Color.RED);
chart.getXAxis().setTextColor(Color.BLUE);
chart.getAxisLeft().setTextColor(Color.GREEN);
chart.getAxisRight().setTextColor(Color.GREEN);
Legend legend = chart.getLegend();
legend.setTextColor(Color.MAGENTA);
parent.addView(chart);
}
} }
@@ -185,6 +300,7 @@ ScoutingVersion {
public int get_byte_id() {return dropdownType;} public int get_byte_id() {return dropdownType;}
public inputTypes getInputType(){return inputTypes.DROPDOWN;} public inputTypes getInputType(){return inputTypes.DROPDOWN;}
public valueTypes getValueType(){return valueTypes.NUM;} public valueTypes getValueType(){return valueTypes.NUM;}
public Object get_fallback_value(){return 0;}
public dropdownType(){}; public dropdownType(){};
public dropdownType(String name, String[] text_options, int defaultSelIndex){ public dropdownType(String name, String[] text_options, int defaultSelIndex){
super(name); super(name);
@@ -269,6 +385,17 @@ ScoutingVersion {
tv.setTextSize(18); tv.setTextSize(18);
parent.addView(tv); parent.addView(tv);
} }
public void add_compiled_view(LinearLayout parent, dataType[] data){
TextView tv = new TextView(parent.getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText("<add pie chart thing here>");
tv.setTextSize(20);
parent.addView(tv);
}
} }
@@ -285,6 +412,7 @@ ScoutingVersion {
public int get_byte_id() {return notesType;} public int get_byte_id() {return notesType;}
public inputTypes getInputType(){return inputTypes.NOTES_INPUT;} public inputTypes getInputType(){return inputTypes.NOTES_INPUT;}
public valueTypes getValueType(){return valueTypes.STRING;} public valueTypes getValueType(){return valueTypes.STRING;}
public Object get_fallback_value(){return "<no-notes>";}
public notesType(){}; public notesType(){};
public notesType(String name, String default_text){ public notesType(String name, String default_text){
super(name); super(name);
@@ -338,6 +466,17 @@ ScoutingVersion {
tv.setTextSize(18); tv.setTextSize(18);
parent.addView(tv); parent.addView(tv);
} }
public void add_compiled_view(LinearLayout parent, dataType[] data){
TextView tv = new TextView(parent.getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText("<add word cloud thing here>");
tv.setTextSize(20);
parent.addView(tv);
}
} }
@@ -14,14 +14,14 @@ public class fields {
public static final ScoutingVersion.inputType[][] default_match_fields = new ScoutingVersion.inputType[][] { public static final ScoutingVersion.inputType[][] default_match_fields = new ScoutingVersion.inputType[][] {
{ {
sv.new sliderType("How good is robot", 5, 0, 10), sv.new sliderType("How good is robot", 5, 1, 10),
sv.new notesType("notes", "<no-notes>"), sv.new notesType("notes", "<no-notes>"),
},{ },{
sv.new sliderType("How good is robot", 5, 0, 10), sv.new sliderType("How good is robot", 5, 1, 10),
sv.new sliderType("Test", 128, 64, 256), sv.new sliderType("Test", 128, 64, 256),
sv.new notesType("notes", "<no-notes>"), sv.new notesType("notes", "<no-notes>"),
},{ },{
sv.new sliderType("How good is robot", 5, 0, 10), sv.new sliderType("How good is robot", 5, 1, 10),
sv.new sliderType("Test", 128, 64, 256), sv.new sliderType("Test", 128, 64, 256),
sv.new dropdownType("test-dropdown", new String[]{"Test1", "test2", "Three"}, 1), sv.new dropdownType("test-dropdown", new String[]{"Test1", "test2", "Three"}, 1),
sv.new notesType("notes", "<no-notes>"), sv.new notesType("notes", "<no-notes>"),
@@ -4,6 +4,8 @@ import android.content.Context;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.Gravity; import android.view.Gravity;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TableLayout; import android.widget.TableLayout;
@@ -13,6 +15,7 @@ import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout; import androidx.constraintlayout.widget.ConstraintLayout;
import com.astatin3.scoutingapp2025.SettingsVersionStack.latestSettings;
import com.astatin3.scoutingapp2025.databinding.FragmentDataBinding; import com.astatin3.scoutingapp2025.databinding.FragmentDataBinding;
import com.astatin3.scoutingapp2025.scoutingData.ScoutingDataWriter; import com.astatin3.scoutingapp2025.scoutingData.ScoutingDataWriter;
import com.astatin3.scoutingapp2025.scoutingData.ScoutingVersion; import com.astatin3.scoutingapp2025.scoutingData.ScoutingVersion;
@@ -37,8 +40,10 @@ public class searchView extends ConstraintLayout {
frcEvent event; frcEvent event;
ScoutingVersion.inputType[][] match_values; ScoutingVersion.inputType[][] match_values;
ScoutingVersion.inputType[] latest_match_values;
ScoutingVersion.transferType[][] match_transferValues; ScoutingVersion.transferType[][] match_transferValues;
ScoutingVersion.inputType[][] pit_values; ScoutingVersion.inputType[][] pit_values;
ScoutingVersion.inputType[] latest_pit_values;
ScoutingVersion.transferType[][] pit_transferValues; ScoutingVersion.transferType[][] pit_transferValues;
public void init(FragmentDataBinding binding, frcEvent event){ public void init(FragmentDataBinding binding, frcEvent event){
@@ -48,8 +53,10 @@ public class searchView extends ConstraintLayout {
match_values = fields.load(fields.matchFieldsFilename); match_values = fields.load(fields.matchFieldsFilename);
latest_match_values = match_values[match_values.length-1];
match_transferValues = fields.sv.get_transfer_values(match_values); match_transferValues = fields.sv.get_transfer_values(match_values);
pit_values = fields.load(fields.pitsFieldsFilename); pit_values = fields.load(fields.pitsFieldsFilename);
latest_pit_values = pit_values[pit_values.length-1];
pit_transferValues = fields.sv.get_transfer_values(pit_values); pit_transferValues = fields.sv.get_transfer_values(pit_values);
@@ -102,12 +109,12 @@ public class searchView extends ConstraintLayout {
frcTeam finalTeam = team; frcTeam finalTeam = team;
tr.setOnClickListener(v -> { tr.setOnClickListener(v -> {
loadTeam(finalTeam); loadTeam(finalTeam, latestSettings.settings.get_compiled_mode());
}); });
} }
} }
public void loadTeam(frcTeam team){ public void loadTeam(frcTeam team, boolean compiled_mode){
binding.searchArea.removeAllViews(); binding.searchArea.removeAllViews();
LinearLayout ll = new LinearLayout(getContext()); LinearLayout ll = new LinearLayout(getContext());
@@ -118,6 +125,22 @@ public class searchView extends ConstraintLayout {
ll.setOrientation(LinearLayout.VERTICAL); ll.setOrientation(LinearLayout.VERTICAL);
binding.searchArea.addView(ll); binding.searchArea.addView(ll);
CheckBox cb = new CheckBox(getContext());
cb.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
cb.setText("Compiled mode");
cb.setChecked(compiled_mode);
ll.addView(cb);
cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
latestSettings.settings.set_compiled_mode(isChecked);
loadTeam(team, isChecked);
}
});
TextView tv = new TextView(getContext()); TextView tv = new TextView(getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams( tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
@@ -138,47 +161,82 @@ public class searchView extends ConstraintLayout {
tv.setTextSize(16); tv.setTextSize(16);
ll.addView(tv); ll.addView(tv);
String[] files = fileEditor.getMatchesByTeamNum(team.teamNumber); String[] files = fileEditor.getMatchesByTeamNum(team.teamNumber);
for(int i = 0; i < files.length; i++){ if(files.length == 0){
String[] split = files[i].split("-");
int match_num = Integer.parseInt(split[1]);
ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(files[i], match_values, match_transferValues);
tv = new TextView(getContext()); tv = new TextView(getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams( tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT ViewGroup.LayoutParams.WRAP_CONTENT
)); ));
tv.setPadding(0,20,0,5);
tv.setGravity(Gravity.CENTER_HORIZONTAL); tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText("Match " + (match_num) + " by " + psda.username); tv.setText("No match data has been collected!");
tv.setTextSize(30); tv.setTextSize(23);
ll.addView(tv); ll.addView(tv);
return;
}
if(!compiled_mode){
for (int i = 0; i < files.length; i++) {
String[] split = files[i].split("-");
int match_num = Integer.parseInt(split[1]);
ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(files[i], match_values, match_transferValues);
for(int a = 0 ; a < psda.data.array.length; a++){
tv = new TextView(getContext()); tv = new TextView(getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams( tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT ViewGroup.LayoutParams.WRAP_CONTENT
)); ));
tv.setPadding(0, 20, 0, 5);
tv.setGravity(Gravity.CENTER_HORIZONTAL); tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText(psda.data.array[a].name); tv.setText("Match " + (match_num) + " by " + psda.username);
tv.setTextSize(25); tv.setTextSize(30);
ll.addView(tv); ll.addView(tv);
for (int a = 0; a < psda.data.array.length; a++) {
tv = new TextView(getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText(psda.data.array[a].name);
tv.setTextSize(25);
ll.addView(tv);
match_values[match_values.length-1][a].add_individual_view(ll, psda.data.array[a]);
latest_match_values[a].add_individual_view(ll, psda.data.array[a]);
}
}
} else {
ScoutingVersion.dataType[][] data = new ScoutingVersion.dataType[latest_match_values.length][files.length];
for (int i = 0; i < files.length; i++) {
ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(files[i], match_values, match_transferValues);
for (int a = 0; a < data.length; a++) {
data[a][i] = psda.data.array[a];
}
}
for(int i = 0; i < latest_match_values.length; i++){
tv = new TextView(getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setPadding(0, 20, 0, 5);
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText(latest_match_values[i].name);
tv.setTextSize(30);
ll.addView(tv);
latest_match_values[i].add_compiled_view(ll, data[i]);
} }
} }
System.out.println(ll.getChildCount());
// ScoutingDataWriter.ParsedScoutingDataResult[] scoutingData;
}
public void display_uncompiled_data(){
} }
} }