Add tally counter

This commit is contained in:
Astatin3
2024-07-29 22:29:59 -06:00
parent b4cae9f610
commit 03cbcce2d4
6 changed files with 436 additions and 6 deletions
@@ -2,9 +2,11 @@ package com.astatin3.scoutingapp2025.scoutingData;
import com.astatin3.scoutingapp2025.types.input.dropdownType; import com.astatin3.scoutingapp2025.types.input.dropdownType;
import com.astatin3.scoutingapp2025.types.input.inputType; import com.astatin3.scoutingapp2025.types.input.inputType;
import com.astatin3.scoutingapp2025.types.input.tallyType;
import com.astatin3.scoutingapp2025.types.input.textType; import com.astatin3.scoutingapp2025.types.input.textType;
import com.astatin3.scoutingapp2025.types.input.sliderType; import com.astatin3.scoutingapp2025.types.input.sliderType;
import com.astatin3.scoutingapp2025.types.input.textType; import com.astatin3.scoutingapp2025.types.input.textType;
import com.astatin3.scoutingapp2025.ui.scouting.TallyCounterView;
import com.astatin3.scoutingapp2025.utility.AlertManager; import com.astatin3.scoutingapp2025.utility.AlertManager;
import com.astatin3.scoutingapp2025.utility.fileEditor; import com.astatin3.scoutingapp2025.utility.fileEditor;
import com.astatin3.scoutingapp2025.utility.BuiltByteParser; import com.astatin3.scoutingapp2025.utility.BuiltByteParser;
@@ -31,7 +33,12 @@ public class fields {
new sliderType("Test", 128, 64, 256), new sliderType("Test", 128, 64, 256),
new dropdownType("test-dropdown", new String[]{"Test1", "test2", "Three"}, 1), new dropdownType("test-dropdown", new String[]{"Test1", "test2", "Three"}, 1),
new textType("notes", "<no-notes>"), new textType("notes", "<no-notes>"),
} },{
new tallyType("Test Tally", 0),
new sliderType("Test2", 30, 25, 50),
new dropdownType("test-dropdown", new String[]{"Test1", "test2", "Three"}, 1),
new textType("notes", "<no-notes>"),
}
}; };
public static final inputType[][] default_pit_fields = new inputType[][] { public static final inputType[][] default_pit_fields = new inputType[][] {
@@ -111,11 +118,13 @@ public class fields {
case inputType.notesType: case inputType.notesType:
t = new textType(); t = new textType();
break; break;
case inputType.tallyType:
t = new tallyType();
break;
} }
t.decode((byte[]) obj.get()); t.decode((byte[]) obj.get());
output[i] = t output[i] = t;
;
} }
return output; return output;
@@ -17,11 +17,13 @@ public abstract class inputType {
public static final int slider_type_id = 255; public static final int slider_type_id = 255;
public static final int dropdownType = 254; public static final int dropdownType = 254;
public static final int notesType = 253; public static final int notesType = 253;
public static final int tallyType = 252;
public enum inputTypes { public enum inputTypes {
// USERNAME, // USERNAME,
SLIDER, SLIDER,
DROPDOWN, DROPDOWN,
NOTES_INPUT NOTES_INPUT,
TALLY
} }
public String name; public String name;
public Object default_value; public Object default_value;
@@ -0,0 +1,313 @@
package com.astatin3.scoutingapp2025.types.input;
import android.content.Context;
import android.graphics.Color;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
import com.astatin3.scoutingapp2025.types.data.dataType;
import com.astatin3.scoutingapp2025.types.data.intType;
import com.astatin3.scoutingapp2025.ui.scouting.TallyCounterView;
import com.astatin3.scoutingapp2025.utility.BuiltByteParser;
import com.astatin3.scoutingapp2025.utility.ByteBuilder;
import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.charts.PieChart;
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.data.PieData;
import com.github.mikephil.charting.data.PieDataSet;
import com.github.mikephil.charting.data.PieEntry;
import com.google.android.material.slider.Slider;
import com.skydoves.powerspinner.IconSpinnerAdapter;
import com.skydoves.powerspinner.IconSpinnerItem;
import com.skydoves.powerspinner.OnSpinnerItemSelectedListener;
import com.skydoves.powerspinner.PowerSpinnerView;
import com.skydoves.powerspinner.SpinnerGravity;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
public class tallyType extends inputType {
public int get_byte_id() {return tallyType;}
public inputTypes getInputType(){return inputTypes.TALLY;}
public dataType.valueTypes getValueType(){return dataType.valueTypes.NUM;}
public Object get_fallback_value(){return 0;}
public tallyType(){};
public String get_type_name(){return "Dropdown";}
public tallyType(String name, int default_value){
super(name);
this.default_value = default_value+1;
}
public byte[] encode() throws ByteBuilder.buildingException {
ByteBuilder bb = new ByteBuilder();
bb.addString(name);
bb.addInt((int)default_value);
return bb.build();
}
public void decode(byte[] bytes) throws BuiltByteParser.byteParsingExeption {
BuiltByteParser bbp = new BuiltByteParser(bytes);
ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse();
name = (String) objects.get(0).get();
default_value = objects.get(1).get();
}
public TallyCounterView tally = null;
public View createView(Context context, Function<dataType, Integer> onUpdate){
tally = new TallyCounterView(context);
tally.setOnCountChangedListener(n -> {
onUpdate.apply(getViewValue());
});
setViewValue(default_value);
return tally;
};
public void setViewValue(Object value) {
if(tally == null) return;
System.out.println(value);
if(((int)value) == 0){
nullify();
return;
}
isBlank = false;
tally.setVisibility(View.VISIBLE);
tally.setValue((int)value-1);
}
public void nullify(){
isBlank = true;
tally.setVisibility(View.GONE);
}
public dataType getViewValue(){
if(tally == null) return null;
if(tally.getVisibility() == View.GONE) return new intType(name, 0);
return new intType(name, tally.getValue()+1);
}
public void add_individual_view(LinearLayout parent, dataType data){
if((int)data.get() == 0) return;
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(String.valueOf((int) data.get()-1));
tv.setTextSize(24);
parent.addView(tv);
}
private static float calculateMean(int[] data) {
float sum = 0;
for (int value : data) {
sum += (float) value;
}
return sum / data.length;
}
private static 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 static 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;
}
private static int findMin(dataType[] data){
int min = (int)data[0].get()-1;
for(int i = 1; i < data.length; i++)
if((int)data[i].get()-1 < min)
min = (int)data[i].get()-1;
return min;
}
private static int findMax(dataType[] data){
int max = (int)data[0].get()-1;
for(int i = 1; i < data.length; i++)
if((int)data[i].get()-1 > max)
max = (int)data[i].get()-1;
return max;
}
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 min = findMin(data);
int max = findMax(data);
int[] values = new int[max-min+1];
for (int i = 0; i < data.length; i++)
if((int) data[i].get() != intType.nulval)
values[(int) data[i].get()-min-1]++;
ArrayList<Integer> mean_temp = new ArrayList<>();
for (int i = 0; i < data.length; i++)
if((int)data[i].get() != 0)
mean_temp.add((int) data[i].get()-1);
int[] mean_vals = mean_temp.stream().mapToInt(Integer::intValue).toArray();
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(mean_vals);
float stdDev = calculateStandardDeviation(mean_vals, mean);
// Generate normal distribution curve
List<Entry> normalDistEntries = generateNormalDistribution(mean-min, stdDev, max-min+1, (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.WHITE);
chart.getAxisLeft().setTextColor(Color.WHITE);
chart.getAxisRight().setTextColor(Color.WHITE);
Legend legend = chart.getLegend();
legend.setTextColor(Color.WHITE);
parent.addView(chart);
}
public void add_history_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 min = findMin(data);
int max = findMax(data);
List<Entry> entries = new ArrayList<>();
for (int i = 0; i < data.length; i++){
if(data[i] == null) continue;
entries.add(new Entry(i, (float)(int) data[i].get()-1));
}
LineDataSet dataSet = new LineDataSet(entries, name);
dataSet.setColor(Color.BLUE);
dataSet.setValueTextColor(Color.BLACK);
dataSet.setDrawCircles(false);
dataSet.setDrawValues(false);
LineData lineData = new LineData(dataSet);
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.WHITE);
chart.getAxisLeft().setTextColor(Color.WHITE);
chart.getAxisRight().setTextColor(Color.WHITE);
Legend legend = chart.getLegend();
legend.setTextColor(Color.WHITE);
chart.getAxisLeft().setAxisMinimum(min);
chart.getAxisLeft().setAxisMaximum(max);
chart.getAxisRight().setAxisMinimum(min);
chart.getAxisRight().setAxisMaximum(max);
parent.addView(chart);
}
}
@@ -178,8 +178,8 @@ public class MatchScoutingFragment extends Fragment {
// boolean blank = !latest_values[fi].getViewValue().isNull(); // boolean blank = !latest_values[fi].getViewValue().isNull();
// System.out.println(blank); // System.out.println(blank);
if(asm.isRunning)
asm.update(); update_asm();
if(!DataManager.match_latest_values[fi].isBlank){ if(!DataManager.match_latest_values[fi].isBlank){
tv.setBackgroundColor(0xffff0000); tv.setBackgroundColor(0xffff0000);
@@ -0,0 +1,79 @@
package com.astatin3.scoutingapp2025.ui.scouting;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.astatin3.scoutingapp2025.R;
public class TallyCounterView extends LinearLayout {
private int count = 0;
private TextView countDisplay;
private Button minusButton;
private Button plusButton;
private OnCountChangedListener onCountChangedListener;
public interface OnCountChangedListener {
void onCountChanged(int newCount);
}
public TallyCounterView(Context context) {
super(context);
init(context);
}
public TallyCounterView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public TallyCounterView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
LayoutInflater.from(context).inflate(R.layout.view_tally_counter, this, true);
countDisplay = findViewById(R.id.count_display);
minusButton = findViewById(R.id.minus_button);
plusButton = findViewById(R.id.plus_button);
updateDisplay();
minusButton.setOnClickListener(v -> {
if(count > 0) {
count--;
updateDisplay();
}
});
plusButton.setOnClickListener(v -> {
count++;
updateDisplay();
});
}
private void updateDisplay() {
countDisplay.setText(String.valueOf(count));
if (onCountChangedListener != null) {
onCountChangedListener.onCountChanged(count);
}
}
public void setValue(int value) {
count = value;
updateDisplay();
}
public int getValue() {
return count;
}
public void setOnCountChangedListener(OnCountChangedListener listener) {
this.onCountChangedListener = listener;
}
}
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/minus_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="-" />
<TextView
android:id="@+id/count_display"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:textSize="24sp" />
<Button
android:id="@+id/plus_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="+" />
</LinearLayout>