11 Commits

Author SHA1 Message Date
Daniel Carta 5ddc032be9 Update to 2026
Updates app to 2026, also a few minor UI fixes.
2026-02-10 10:23:05 -07:00
Daniel Carta e6073ded49 Update README.md 2025-11-04 12:03:29 -07:00
Daniel Carta 7503cefa09 Update Screenshots
Changed old screenshots to new ones
2025-11-04 12:01:58 -07:00
Michael Mikovsky 3b4737e6bc Change version 2025-10-08 20:37:30 -06:00
Michael Mikovsky 76d28e46cd Update version to 2.1 2025-10-08 20:27:05 -06:00
Michael Mikovsky 73308f2acc Fix TBA Event coloring 2025-10-08 20:23:11 -06:00
Michael Mikovsky c18a93cb1a Change dashboard if there are no matches 2025-10-08 20:06:25 -06:00
Michael Mikovsky 3ca601f080 Merge pull request #15 from Team4388/increasing-verbosity
Add more comments
2025-10-08 11:57:56 -06:00
Michael Mikovsky 4554db9dc7 Merge branch 'main' into increasing-verbosity 2025-10-08 11:57:48 -06:00
Michael Mikovsky b5b1100c2c Merge pull request #14 from Team4388/random-ui-fixes
Random UI fixes
2025-10-08 11:56:30 -06:00
Daniel Carta ef761003c8 Increased the verbosity 2025-07-24 10:59:22 -06:00
39 changed files with 196 additions and 244 deletions
Vendored
BIN
View File
Binary file not shown.
+15 -5
View File
@@ -7,18 +7,28 @@
[**Read the wiki**](https://github.com/Team4388/ScoutingApp2025/wiki) [**Read the wiki**](https://github.com/Team4388/ScoutingApp2025/wiki)
#### Features [**Test Data**](https://github.com/Team4388/ScoutingApp2025/blob/main/2024week0-1728149849985.scoutbundle)
#### Here is an overview of the main features currently included in the app:
- This project is written for Android! No need for some kind of janky laptop charging setup. - This project is written for Android! No need for some kind of janky laptop charging setup.
- Similar to ScoutingPASS, there are many diffrent types of fields that can be used to collect data. - Similar to ScoutingPASS, many different types of fields can be used to collect data.
- The app is designed to handle updates to the fields on the fly, without loosing any data! - The app is designed to handle updates to the fields on the fly, without losing any data!
- Unlike other scouting solutions, scouters can disable any field they did not measure, and disabled fields will not be included in any calculations. - Unlike other scouting solutions, scouters can disable any field they did not measure, and disabled fields will not be included in any calculations.
- Dynamic displays based off of the diffrent fields. - Dynamic displays based on the different fields.
- Data transfer including 2D codes, Bluetooth, and File Bundle. - Data transfer including 2D codes, Bluetooth, and File Bundle.
- Exporting using CSV. - Exporting using CSV.
- Deployment on F-Droid - Deployment on F-Droid
- Data cloud sync using an FTP server - Data cloud sync using an FTP server
#### Things that are yet to be implemented:
- A page that lets users cross-compare scouting data between teams. (Compare)
#### Things that may or may not be implemented:
- Statbotics integration
- Scout error estimation using OPR-like calculation
- - Would most likely require Statbotics
https://www.thebluealliance.com/avatars
### Screenshots ### Screenshots
|Match scouting interface|Field editor|Teams data viewer| |Match scouting interface|Field editor|Teams data viewer|
|-|-|-| |-|-|-|
|![Screenshot1](https://github.com/Team4388/ScoutingApp2025/blob/main/metadata/en-US/images/phoneScreenshots/1.png?raw=true)|![Screenshot2](https://github.com/Team4388/ScoutingApp2025/blob/main/metadata/en-US/images/phoneScreenshots/2.png?raw=true)|![Screenshot3](https://github.com/Team4388/ScoutingApp2025/blob/main/metadata/en-US/images/phoneScreenshots/3.png?raw=true)| |![Screenshot1](https://github.com/Team4388/ScoutingApp2025/blob/main/metadata/en-US/images/phoneScreenshots/1.png?raw=true)|![Screenshot2](https://github.com/Team4388/ScoutingApp2025/blob/main/metadata/en-US/images/phoneScreenshots/2.png?raw=true)|![Screenshot3](https://github.com/Team4388/ScoutingApp2025/blob/main/metadata/en-US/images/phoneScreenshots/3.png?raw=true)|
BIN
View File
Binary file not shown.
+3 -3
View File
@@ -16,7 +16,7 @@ android {
dependenciesInfo { dependenciesInfo {
// Disables dependency metadata when building APKs. // Disables dependency metadata when building APKs.
includeInApk = false includeInApk = false
// Disables dependency metadata when building Android App Bundles. // Disables dependency metadata when building Android App Bundles.5
includeInBundle = false includeInBundle = false
} }
@@ -25,8 +25,8 @@ android {
applicationId = "com.ridgebotics.ridgescout" applicationId = "com.ridgebotics.ridgescout"
minSdk = 24 minSdk = 24
targetSdk = 34 targetSdk = 34
versionCode = 11 // **IMPORTANT** Increment this before releasing on github versionCode = 12 // **IMPORTANT** Increment this before releasing on github
versionName = "1.4"// **IMPORTANT** Change this before releasing on github (<Year num since 2024>.<Update Version>) versionName = "2.0"// **IMPORTANT** Change this before releasing on github (<Year num since 2024>.<Update Version>)
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
@@ -30,7 +30,7 @@ public class Fields {
public static final FieldType[][] default_match_fields = new FieldType[][] { public static final FieldType[][] default_match_fields = new FieldType[][] {
{ {
new FieldposType(uuid(),"Auto start pos", "Where does the robot start its auto?", FieldposType.DEFAULT_FIELD_IMAGE, new int[]{0,0}), new FieldposType(uuid(),"Auto start pos", "Where does the robot start its auto?", new int[]{0,0}),
new TallyType(uuid(),"Auto L4 Coral", "How many coral did this robot score in L4 during auto?", 0), new TallyType(uuid(),"Auto L4 Coral", "How many coral did this robot score in L4 during auto?", 0),
new TallyType(uuid(),"Auto L3 Coral", "How many coral did this robot score in L3 during auto?", 0), new TallyType(uuid(),"Auto L3 Coral", "How many coral did this robot score in L3 during auto?", 0),
@@ -42,12 +42,12 @@ public class Fields {
new DropdownType(uuid(),"Auto Quality", "How did the robot drive during auto?", new String[]{"Smooth", "Jittery"}, 0), new DropdownType(uuid(),"Auto Quality", "How did the robot drive during auto?", new String[]{"Smooth", "Jittery"}, 0),
new TextType(uuid(),"Auto Comments", "Anything interesting about auto", ""), new TextType(uuid(),"Auto Comments", "Anything interesting about auto", ""),
new TallyType(uuid(),"Teleop L4 Coral", "How many coral did this robot score in L4 during teleop?", 0), new TallyType(uuid(),"Teleop L4 Coral", "How many coral did this robot score in L4 during auto?", 0),
new TallyType(uuid(),"Teleop L3 Coral", "How many coral did this robot score in L3 during teleop?", 0), new TallyType(uuid(),"Teleop L3 Coral", "How many coral did this robot score in L3 during auto?", 0),
new TallyType(uuid(),"Teleop L2 Coral", "How many coral did this robot score in L2 during teleop?", 0), new TallyType(uuid(),"Teleop L2 Coral", "How many coral did this robot score in L2 during auto?", 0),
new TallyType(uuid(),"Teleop L1 Coral", "How many coral did this robot score in L1 during teleop?", 0), new TallyType(uuid(),"Teleop L1 Coral", "How many coral did this robot score in L1 during auto?", 0),
new TallyType(uuid(),"Teleop Processor Algae", "How many algae did this robot score in the Barge during teleop?", 0), new TallyType(uuid(),"Teleop Processor Algae", "How many algae did this robot score in the Barge during auto?", 0),
new TallyType(uuid(),"Teleop Barge Algae", "How many algae did this robot score in the Barge during teleop?", 0), new TallyType(uuid(),"Teleop Barge Algae", "How many algae did this robot score in the Barge during auto?", 0),
new CheckboxType(uuid(),"Upper Algae Removal", "Did the robot remove upper Algae?", 0), new CheckboxType(uuid(),"Upper Algae Removal", "Did the robot remove upper Algae?", 0),
new CheckboxType(uuid(),"Lower Algae Removal", "Did the robot remove lower Algae?", 0), new CheckboxType(uuid(),"Lower Algae Removal", "Did the robot remove lower Algae?", 0),
@@ -41,7 +41,6 @@ public class ScoutingArray {
continue; continue;
case CREATE: case CREATE:
new_values[i] = create_transfer((CreateTransferType) tv); new_values[i] = create_transfer((CreateTransferType) tv);
continue;
} }
} }
this.array = new_values; this.array = new_values;
@@ -72,12 +71,6 @@ public class ScoutingArray {
return get_data_type_by_UUID(tv.UUID); return get_data_type_by_UUID(tv.UUID);
} }
// private dataType rename_transfer(renameTransferType tv){
// dataType dt = get_data_type_by_name(tv.name);
// dt.name = tv.new_name;
// return dt;
// }
private RawDataType create_transfer(CreateTransferType tv){ private RawDataType create_transfer(CreateTransferType tv){
FieldType it = get_input_type_by_UUID(version+1, tv.UUID); FieldType it = get_input_type_by_UUID(version+1, tv.UUID);
switch (it.getValueType()){ switch (it.getValueType()){
@@ -32,12 +32,6 @@ public class ScoutingFile {
ByteBuilder bb = new ByteBuilder() ByteBuilder bb = new ByteBuilder()
.addString(filename); .addString(filename);
// byte[] data = Objects.requireNonNull(fileEditor.readFile(filename));
// for(int i = 0; i < data.length / 65535; i++){
// bb.addRaw(255, fileEditor.getByteBlock(data, i*65535, (i+1)*65535));
// }
bb.addRaw(255, Objects.requireNonNull(FileEditor.readFile(filename))); bb.addRaw(255, Objects.requireNonNull(FileEditor.readFile(filename)));
return bb.build(); return bb.build();
@@ -16,11 +16,14 @@ import java.util.stream.IntStream;
// Class to contain data for an entire event. // Class to contain data for an entire event.
// Easily encoded and decoded to binary format. // Easily encoded and decoded to binary format.
public class frcEvent { public class frcEvent {
public String eventCode;
// public static final int typecode = 254; Unused, no idea what this is
public String eventCode; //Current event code
public String name; public String name;
public ArrayList<frcMatch> matches; public ArrayList<frcMatch> matches;
public ArrayList<frcTeam> teams; public ArrayList<frcTeam> teams;
// Turns frcEvent into raw data
public byte[] encode() { public byte[] encode() {
try { try {
ByteBuilder bb = new ByteBuilder() ByteBuilder bb = new ByteBuilder()
@@ -46,6 +49,7 @@ public class frcEvent {
} }
} }
//Decodes the frcEvent
public static frcEvent decode(byte[] bytes) { public static frcEvent decode(byte[] bytes) {
try { try {
ArrayList<BuiltByteParser.parsedObject> objects = ArrayList<BuiltByteParser.parsedObject> objects =
@@ -74,6 +78,7 @@ public class frcEvent {
} }
} }
//Generates text
@NonNull @NonNull
public String toString() { public String toString() {
return ( return (
@@ -207,6 +207,7 @@ public class CheckboxType extends FieldType {
parent.addView(chart); parent.addView(chart);
} }
//TODO
public void addDataToTable(TableLayout parent, Map<Integer, List<RawDataType>> data){ public void addDataToTable(TableLayout parent, Map<Integer, List<RawDataType>> data){
} }
@@ -103,7 +103,7 @@ public class DropdownType extends FieldType {
// Dropdown view
public void add_individual_view(LinearLayout parent, RawDataType data){ public void add_individual_view(LinearLayout parent, RawDataType data){
if(data.isNull()) return; if(data.isNull()) return;
@@ -112,7 +112,7 @@ public class DropdownType extends FieldType {
.layout_match_wrap() .layout_match_wrap()
.padding(20) .padding(20)
.size(18) .size(18)
.align_center() .align_left()
.build()); .build());
} }
@@ -122,7 +122,7 @@ public class DropdownType extends FieldType {
// Generates N amount of colors, all opposite colors
private static int[] generateEquidistantColors(int N) { private static int[] generateEquidistantColors(int N) {
int[] colors = new int[N]; int[] colors = new int[N];
float[] hsv = new float[3]; // Hue, Saturation, Value float[] hsv = new float[3]; // Hue, Saturation, Value
@@ -138,6 +138,7 @@ public class DropdownType extends FieldType {
return colors; return colors;
} }
// Turns the dropdown into a pie chart in the compiled view
public void add_compiled_view(LinearLayout parent, RawDataType[] data){ public void add_compiled_view(LinearLayout parent, RawDataType[] data){
PieChart chart = new PieChart(parent.getContext()); PieChart chart = new PieChart(parent.getContext());
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams( FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(
@@ -171,7 +172,7 @@ public class DropdownType extends FieldType {
// Turns the dropdown into a line chart in the history view
public void add_history_view(LinearLayout parent, RawDataType[] data){ public void add_history_view(LinearLayout parent, RawDataType[] data){
LineChart chart = new LineChart(parent.getContext()); LineChart chart = new LineChart(parent.getContext());
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams( FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(
@@ -238,6 +239,7 @@ public class DropdownType extends FieldType {
parent.addView(chart); parent.addView(chart);
} }
//TODO
public void addDataToTable(TableLayout parent, Map<Integer, List<RawDataType>> data){ public void addDataToTable(TableLayout parent, Map<Integer, List<RawDataType>> data){
} }
@@ -18,7 +18,6 @@ import com.github.mikephil.charting.components.Legend;
import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.data.LineDataSet; import com.github.mikephil.charting.data.LineDataSet;
import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.types.data.RawDataType; import com.ridgebotics.ridgescout.types.data.RawDataType;
import com.ridgebotics.ridgescout.types.data.IntArrType; import com.ridgebotics.ridgescout.types.data.IntArrType;
import com.ridgebotics.ridgescout.ui.views.FieldPosView; import com.ridgebotics.ridgescout.ui.views.FieldPosView;
@@ -32,58 +31,14 @@ import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
public class FieldposType extends FieldType { public class FieldposType extends FieldType {
public static final FieldImage DEFAULT_FIELD_IMAGE = FieldImage.F2025;
public enum FieldImage {
F2025(0, "2025", R.drawable.field_2025, R.drawable.field_2025_flipped),
F2025_analogous(1, "2025 - analogous", R.drawable.field_2025_analogous);
public int index, resId_normal, resId_flipped;
public String name;
public boolean flippable;
FieldImage(int index, String name, int resId) {
this.index = index;
this.name = name;
this.resId_normal = resId;
this.resId_flipped = resId;
this.flippable = false;
}
FieldImage(int index, String name, int resId_normal, int resId_flipped) {
this.index = index;
this.name = name;
this.resId_normal = resId_normal;
this.resId_flipped = resId_flipped;
this.flippable = true;
}
public static FieldImage from_index(int index) {
return FieldImage.values()[index];
}
public int get_index() {
return this.index;
}
@Override
public String toString() {
return name;
}
}
public FieldImage fieldImage;
public int get_byte_id() {return fieldposType;} public int get_byte_id() {return fieldposType;}
public inputTypes getInputType(){return inputTypes.FIELDPOS;} public inputTypes getInputType(){return inputTypes.FIELDPOS;}
public RawDataType.valueTypes getValueType(){return RawDataType.valueTypes.NUM;} public RawDataType.valueTypes getValueType(){return RawDataType.valueTypes.NUM;}
public Object get_fallback_value(){return 0;} public Object get_fallback_value(){return 0;}
public FieldposType(){} public FieldposType(){}
public String get_type_name(){return "Field Pos";} public String get_type_name(){return "Field Pos";}
public FieldposType(String UUID, String name, String description, FieldImage fieldImage, int[] default_value){ public FieldposType(String UUID, String name, String description, int[] default_value){
super(UUID, name, description); super(UUID, name, description);
this.fieldImage = fieldImage;
this.default_value = default_value; this.default_value = default_value;
} }
@@ -92,14 +47,11 @@ public class FieldposType extends FieldType {
public void encodeData(ByteBuilder bb) throws ByteBuilder.buildingException { public void encodeData(ByteBuilder bb) throws ByteBuilder.buildingException {
bb.addInt(this.fieldImage.get_index());
bb.addIntArray((int[]) default_value); bb.addIntArray((int[]) default_value);
} }
public void decodeData(ArrayList<BuiltByteParser.parsedObject> objects) { public void decodeData(ArrayList<BuiltByteParser.parsedObject> objects) {
fieldImage = FieldImage.from_index((int) objects.get(0).get()); default_value = objects.get(0).get();
default_value = objects.get(1).get();
} }
@@ -113,7 +65,6 @@ public class FieldposType extends FieldType {
onUpdate.apply(new IntArrType(name, pos)); onUpdate.apply(new IntArrType(name, pos));
}); });
setViewValue(default_value); setViewValue(default_value);
field.setFieldImage(fieldImage);
return field; return field;
} }
@@ -151,7 +102,6 @@ public class FieldposType extends FieldType {
FieldPosView fp = new FieldPosView(parent.getContext()); FieldPosView fp = new FieldPosView(parent.getContext());
fp.setEnabled(false); fp.setEnabled(false);
fp.setPos((int[]) data.get()); fp.setPos((int[]) data.get());
fp.setFieldImage(this.fieldImage);
parent.addView(fp); parent.addView(fp);
} }
@@ -163,13 +113,54 @@ public class FieldposType extends FieldType {
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 += (float) 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 y = (float) ((1 / (stdDev * Math.sqrt(2 * Math.PI)))
* Math.exp(-0.5 * Math.pow(((float) i - mean) / stdDev, 2)));
entries.add(new Entry((float) i, y*scale)); // Scale y for visibility
}
return entries;
}
private static int findMin(RawDataType[] data){
int min = (int)data[0].get();
for(int i = 1; i < data.length; i++)
if((int)data[i].get() < min)
min = (int)data[i].get();
return min;
}
private static int findMax(RawDataType[] data){
int max = (int)data[0].get();
for(int i = 1; i < data.length; i++)
if((int)data[i].get() > max)
max = (int)data[i].get();
return max;
}
public void add_compiled_view(LinearLayout parent, RawDataType[] data){ public void add_compiled_view(LinearLayout parent, RawDataType[] data){
MultiFieldPosView mfp = new MultiFieldPosView(parent.getContext()); MultiFieldPosView mfp = new MultiFieldPosView(parent.getContext());
for(int i = 0; i < data.length; i++){ for(int i = 0; i < data.length; i++){
if(data[i].isNull()) continue; if(data[i].isNull()) continue;
mfp.addPos((int[]) data[i].get()); mfp.addPos((int[]) data[i].get());
} }
mfp.setFieldImage(fieldImage);
parent.addView(mfp); parent.addView(mfp);
} }
@@ -230,6 +221,7 @@ public class FieldposType extends FieldType {
parent.addView(chart); parent.addView(chart);
} }
//TODO
public void addDataToTable(TableLayout parent, Map<Integer, List<RawDataType>> data){ public void addDataToTable(TableLayout parent, Map<Integer, List<RawDataType>> data){
} }
@@ -121,7 +121,7 @@ public class NumberType extends FieldType {
if(data.isNull()) return; if(data.isNull()) return;
parent.addView(new TextViewBuilder(parent.getContext(), String.valueOf((int) data.get())) parent.addView(new TextViewBuilder(parent.getContext(), String.valueOf((int) data.get()))
.layout_match_wrap() .layout_match_wrap()
.align_center() .align_left()
.size(24) .size(24)
.build()); .build());
} }
@@ -312,6 +312,7 @@ public class NumberType extends FieldType {
parent.addView(chart); parent.addView(chart);
} }
//TODO
public void addDataToTable(TableLayout parent, Map<Integer, List<RawDataType>> data){ public void addDataToTable(TableLayout parent, Map<Integer, List<RawDataType>> data){
} }
@@ -106,7 +106,7 @@ public class TallyType extends FieldType {
if(data.isNull()) return; if(data.isNull()) return;
parent.addView(new TextViewBuilder(parent.getContext(), String.valueOf((int) data.get())) parent.addView(new TextViewBuilder(parent.getContext(), String.valueOf((int) data.get()))
.layout_match_wrap() .layout_match_wrap()
.align_center() .align_left()
.size(24) .size(24)
.build()); .build());
} }
@@ -115,7 +115,7 @@ public class TextType extends FieldType {
if(data.isNull()) return; if(data.isNull()) return;
parent.addView(new TextViewBuilder(parent.getContext(), (String) data.get()) parent.addView(new TextViewBuilder(parent.getContext(), (String) data.get())
.layout_match_wrap() .layout_match_wrap()
.align_center() .align_left()
.size(18) .size(18)
.build()); .build());
} }
@@ -220,6 +220,7 @@ public class TextType extends FieldType {
} }
//TODO
public void addDataToTable(TableLayout parent, Map<Integer, List<RawDataType>> data){ public void addDataToTable(TableLayout parent, Map<Integer, List<RawDataType>> data){
} }
@@ -11,6 +11,7 @@ import static com.ridgebotics.ridgescout.utility.DataManager.pit_transferValues;
import static com.ridgebotics.ridgescout.utility.DataManager.pit_values; import static com.ridgebotics.ridgescout.utility.DataManager.pit_values;
import android.content.Intent; import android.content.Intent;
import android.graphics.Paint;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.view.Gravity; import android.view.Gravity;
@@ -221,19 +222,18 @@ public class TeamsFragment extends Fragment {
ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(files[matchIndex], match_values, match_transferValues); ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(files[matchIndex], match_values, match_transferValues);
binding.matchArea.addView( TextView title = new TextViewBuilder(getContext(),
new TextViewBuilder(getContext(), "M" + (match_num) + " " + split[2] + "-" + split[3] + " by " + psda.username) "M" + (match_num) + " " + split[2] + "-" + split[3] + " by " + psda.username)
.align_center() .align_center()
.size(30) .size(30)
.padding(0,0,40,5) .padding(0, 0, 40, 5)
.build() .build();
title.setPaintFlags(title.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
); binding.matchArea.addView(title);
for (int i = 0; i < psda.data.array.length; i++) { for (int i = 0; i < psda.data.array.length; i++) {
TextViewBuilder tv = new TextViewBuilder(getContext(), match_latest_values[i].name) TextViewBuilder tv = new TextViewBuilder(getContext(), match_latest_values[i].name)
.align_center() .align_left()
.size(25); .size(25);
if (psda.data.array[i].isNull()) { if (psda.data.array[i].isNull()) {
@@ -282,6 +282,7 @@ public class TeamsFragment extends Fragment {
.build() .build()
); );
if(data[i] != null) if(data[i] != null)
match_latest_values[i].add_compiled_view(binding.matchArea, data[i]); match_latest_values[i].add_compiled_view(binding.matchArea, data[i]);
} }
@@ -307,13 +308,13 @@ public class TeamsFragment extends Fragment {
for(int i = 0; i < match_latest_values.length; i++){ for(int i = 0; i < match_latest_values.length; i++){
binding.matchArea.addView( TextView tv = new TextViewBuilder(getContext(), match_latest_values[i].name)
new TextViewBuilder(getContext(), match_latest_values[i].name)
.align_center() .align_center()
.size(30) .size(30)
.padding(0,0,20,5) .padding(0,0,20,5)
.build() .build();
); tv.setPaintFlags(tv.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
binding.matchArea.addView(tv);
if(data[i] != null) if(data[i] != null)
match_latest_values[i].add_history_view(binding.matchArea, data[i]); match_latest_values[i].add_history_view(binding.matchArea, data[i]);
@@ -157,28 +157,35 @@ public class ScoutingFragment extends Fragment {
private void updateDashboard() { private void updateDashboard() {
binding.textName.setText("Welcome, " + SettingsManager.getUsername() + "!"); binding.textName.setText("Welcome, " + SettingsManager.getUsername() + "!");
int curMatchNum = SettingsManager.getMatchNum();
int nextMatch;
int teamNum = SettingsManager.getTeamNum();
try {
nextMatch = event.getNextTeamMatch(teamNum, curMatchNum).matchIndex;
} catch (Exception e){
AlertManager.error("Sorry, in event ("+evcode+"), your team number ("+teamNum+") wasn't found!", e);
return;
}
binding.textMatchAlliance.setText("Match: " + (curMatchNum+1) + ", " + SettingsManager.getAllyPos());
binding.textRescoutIndicator.setText("Things to rescout: " + DataManager.rescout_list.size()); binding.textRescoutIndicator.setText("Things to rescout: " + DataManager.rescout_list.size());
binding.infoBox.addView(new TextViewBuilder(getContext(), "Our next match: Match " + nextMatch) if(event.matches.size() == 0) {
.body1() binding.textMatchAlliance.setText("No Matches!");
.build()); } else {
int teamNum = SettingsManager.getTeamNum();
int curMatchNum = SettingsManager.getMatchNum();
int informedBy = event.getMostInformedBy(teamNum, curMatchNum); binding.textMatchAlliance.setText("Match: " + (curMatchNum+1) + ", " + SettingsManager.getAllyPos());
int nextMatch;
try {
nextMatch = event.getNextTeamMatch(teamNum, curMatchNum).matchIndex;
} catch (Exception e){
AlertManager.error("Sorry, in event ("+evcode+"), your team number ("+teamNum+") wasn't found!", e);
return;
}
binding.infoBox.addView(new TextViewBuilder(getContext(), "Most informed by: Match " + informedBy) binding.infoBox.addView(new TextViewBuilder(getContext(), "Our next match: Match " + nextMatch)
.body1() .body1()
.build()); .build());
int informedBy = event.getMostInformedBy(teamNum, curMatchNum);
binding.infoBox.addView(new TextViewBuilder(getContext(), "Most informed by: Match " + informedBy)
.body1()
.build());
}
} }
} }
@@ -18,14 +18,10 @@ import com.ridgebotics.ridgescout.types.input.NumberType;
import com.ridgebotics.ridgescout.types.input.SliderType; import com.ridgebotics.ridgescout.types.input.SliderType;
import com.ridgebotics.ridgescout.types.input.TallyType; import com.ridgebotics.ridgescout.types.input.TallyType;
import com.ridgebotics.ridgescout.types.input.TextType; import com.ridgebotics.ridgescout.types.input.TextType;
import com.ridgebotics.ridgescout.ui.views.CustomSpinnerView;
import com.ridgebotics.ridgescout.utility.AlertManager; import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.builders.TextViewBuilder; import com.ridgebotics.ridgescout.utility.builders.TextViewBuilder;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID; import java.util.UUID;
// Class to help with fields editor fragment, containing the defaults for each field. // Class to help with fields editor fragment, containing the defaults for each field.
@@ -34,8 +30,7 @@ public class FieldEditorHelper {
paramNumber, paramNumber,
paramString, paramString,
paramStringArray, paramStringArray,
paramNumberArray, paramNumberArray
paramDropdown
} }
public static class parameterType { public static class parameterType {
@@ -70,17 +65,6 @@ public class FieldEditorHelper {
} }
} }
public static class paramDropdown extends parameterType{
public List<String> options;
public int val;
public paramDropdown(String name, List<String> options, int val){
this.name = name + " (Dropdown)";
this.options = options;
this.val = val;
this.id = parameterTypeEnum.paramDropdown;
}
}
// public static class paramNumberArray extends parameterType{ // public static class paramNumberArray extends parameterType{
// public int[] val; // public int[] val;
// public paramNumberArray(String name, int[] val){ // public paramNumberArray(String name, int[] val){
@@ -183,16 +167,9 @@ public class FieldEditorHelper {
} }
private static parameterType[] getFieldPosParam(FieldposType s){ private static parameterType[] getFieldPosParam(FieldposType s){
FieldposType.FieldImage[] f_images = FieldposType.FieldImage.values();
List<String> images = new ArrayList<>();
for (FieldposType.FieldImage fimage: f_images) {
images.add(fimage.toString());
}
return new parameterType[]{ return new parameterType[]{
new paramString("Name", s.name), new paramString("Name", s.name),
new paramString("Description", s.description), new paramString("Description", s.description),
new paramDropdown("Field Image", images, 0),
new paramNumber("Default X", ((int[]) s.default_value)[0]), new paramNumber("Default X", ((int[]) s.default_value)[0]),
new paramNumber("Default Y", ((int[]) s.default_value)[1]) new paramNumber("Default Y", ((int[]) s.default_value)[1])
}; };
@@ -242,10 +219,9 @@ public class FieldEditorHelper {
public static void setFieldPosParam(FieldposType s, parameterType[] types){ public static void setFieldPosParam(FieldposType s, parameterType[] types){
s.name = ((paramString) types[0]).val; s.name = ((paramString) types[0]).val;
s.description = ((paramString) types[1]).val; s.description = ((paramString) types[1]).val;
s.fieldImage = FieldposType.FieldImage.from_index(((paramDropdown) types[2]).val);
s.default_value = new int[]{ s.default_value = new int[]{
((paramNumber) types[3]).val, ((paramNumber) types[2]).val,
((paramNumber) types[4]).val ((paramNumber) types[3]).val
}; };
} }
@@ -331,24 +307,6 @@ public class FieldEditorHelper {
return text; return text;
} }
private static View createDropdown(Context c, String name, List<String> options, int value){
CustomSpinnerView spinner = new CustomSpinnerView(c);
spinner.setTitle(name);
spinner.setOptions(options, value);
spinner.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
// EditText text = new EditText(c);
// text.setText(String.join("\n", value));
// text.setLayoutParams(new LinearLayout.LayoutParams(
// ViewGroup.LayoutParams.MATCH_PARENT,
// ViewGroup.LayoutParams.WRAP_CONTENT
// ));
return spinner;
}
private static View createEdit(Context c, parameterType t){ private static View createEdit(Context c, parameterType t){
switch (t.id){ switch (t.id){
case paramNumber: case paramNumber:
@@ -357,8 +315,6 @@ public class FieldEditorHelper {
return createStringEdit(c, ((paramString) t).val); return createStringEdit(c, ((paramString) t).val);
case paramStringArray: case paramStringArray:
return createStringArrayEdit(c, ((paramStringArray) t).val); return createStringArrayEdit(c, ((paramStringArray) t).val);
case paramDropdown:
return createDropdown(c, t.name, ((paramDropdown) t).options, ((paramDropdown) t).val);
} }
return null; return null;
} }
@@ -366,27 +322,22 @@ public class FieldEditorHelper {
private static boolean readEdit(View v, parameterType t){ private static boolean readEdit(View v, parameterType t){
try{ try{
// String val; String val;
switch (t.id) { switch (t.id) {
case paramNumber: case paramNumber:
String val1 = ((EditText) v).getText().toString(); val = ((EditText) v).getText().toString();
if(val1.isEmpty() || val1.isBlank()) return false; if(val.isEmpty() || val.isBlank()) return false;
((paramNumber) t).val = Integer.parseInt(val1); ((paramNumber) t).val = Integer.parseInt(val);
break; break;
case paramString: case paramString:
String val2 = ((EditText) v).getText().toString(); val = ((EditText) v).getText().toString();
//if(val.isEmpty() || val.isBlank()) return false; //if(val.isEmpty() || val.isBlank()) return false;
((paramString) t).val = val2; ((paramString) t).val = val;
break; break;
case paramStringArray: case paramStringArray:
String val3 = ((EditText) v).getText().toString(); val = ((EditText) v).getText().toString();
if(val3.isEmpty() || val3.isBlank()) return false; if(val.isEmpty() || val.isBlank()) return false;
((paramStringArray) t).val = val3.split("\n"); ((paramStringArray) t).val = val.split("\n");
break;
case paramDropdown:
int val4 = ((CustomSpinnerView) v).getIndex();
// if(val.isEmpty() || val.isBlank()) return false;
((paramDropdown) t).val = val4;
break; break;
} }
} catch (Exception e) { } catch (Exception e) {
@@ -151,7 +151,12 @@ public class SettingsFragment extends Fragment {
manager.addItem(new CheckboxSettingsItem(EnableQuickAllianceChangeKey, "Enable quick alliance swap", null)); manager.addItem(new CheckboxSettingsItem(EnableQuickAllianceChangeKey, "Enable quick alliance swap", null));
manager.addItem(new CheckboxSettingsItem(FieldImageKey, "Field Image", null)); manager.addItem(new DropdownSettingsItem(FieldImageKey, "Field Image", new String[]{
"2026",
"2026 (Flipped)",
"2025",
"2025 (Flipped)"
}));
manager.addItem(new DropdownSettingsItem(AllyPosKey, "Alliance Pos", alliance_pos_list)); manager.addItem(new DropdownSettingsItem(AllyPosKey, "Alliance Pos", alliance_pos_list));
@@ -165,12 +165,12 @@ public class TBASelectorFragment extends Fragment {
try { try {
Date startDate = format.parse(j.getString("start_date")); Date startDate = format.parse(j.getString("start_date"));
Date endDate = format.parse(j.getString("end_date")); Date endDate = format.parse(j.getString("end_date"));
if(currentTime.after(endDate)){ if(currentTime.after(startDate) && currentTime.before(endDate)) {
row.setColor(tba_current);
} else if(currentTime.after(endDate)){
row.setColor(tba_previous); row.setColor(tba_previous);
}else if(currentTime.before(startDate)){ }else if(currentTime.before(startDate)){
row.setColor(tba_next); row.setColor(tba_next);
}else if(currentTime.after(startDate) && currentTime.before(endDate)){
row.setColor(tba_current);
} }
} catch (Exception e) { } catch (Exception e) {
AlertManager.error("Failed finding start and end dates!", e); AlertManager.error("Failed finding start and end dates!", e);
@@ -13,7 +13,6 @@ import android.widget.FrameLayout;
import android.widget.ImageView; import android.widget.ImageView;
import com.ridgebotics.ridgescout.R; import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.types.input.FieldposType;
import com.ridgebotics.ridgescout.utility.SettingsManager; import com.ridgebotics.ridgescout.utility.SettingsManager;
// Custom view to display a field position. // Custom view to display a field position.
@@ -25,8 +24,6 @@ public class FieldPosView extends FrameLayout {
private ImageView imageView; private ImageView imageView;
private boolean enabled = true; private boolean enabled = true;
private boolean flip = false;
public interface onTapListener { public interface onTapListener {
void onUpdate(int[] pos); void onUpdate(int[] pos);
}; };
@@ -68,21 +65,30 @@ public class FieldPosView extends FrameLayout {
// Set touch listener // Set touch listener
setOnTouchListener((v, event) -> { setOnTouchListener((v, event) -> {
if (enabled && event.getAction() == MotionEvent.ACTION_DOWN) { if (enabled && event.getAction() == MotionEvent.ACTION_DOWN) {
// If the field image is rotated, rotate the input and output x = (int) ((event.getX()/getWidth())*255);
if(!flip) { y = (int) ((event.getY()/getHeight())*255);
x = (int) (event.getX() / getWidth()) * 255;
y = (int) (event.getY() / getHeight()) * 255;
} else {
x = (int) (1 - (event.getX() / getWidth())) * 255;
y = (int) (1 - (event.getY() / getHeight())) * 255;
}
System.out.println("X: " + x + ", Y: " + y);
tl.onUpdate(getPos()); tl.onUpdate(getPos());
invalidate(); invalidate();
return true; return true;
} }
return false; return false;
}); });
switch(SettingsManager.getFieldImageIndex()){
case "2025":
setImageResource(R.drawable.field_2025);
break;
case "2025 (Flipped)":
setImageResource(R.drawable.field_2025_flipped);
break;
case "2026":
setImageResource(R.drawable.field_2026);
break;
case "2026 (Flipped)":
setImageResource(R.drawable.field_2026_flipped);
break;
}
} }
public void setPos(int[] pos){ public void setPos(int[] pos){
@@ -96,28 +102,10 @@ public class FieldPosView extends FrameLayout {
protected void dispatchDraw(Canvas canvas) { protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas); super.dispatchDraw(canvas);
if (x >= 0 && y >= 0) { if (x >= 0 && y >= 0) {
canvas.drawCircle(
float cx; ((float) x /255)*getWidth(),
float cy; ((float) y /255)*getHeight(),
// If the field image is rotated, rotate the input and output POINT_RADIUS, paint);
if(!flip) {
cx = ((float) x / 255)*getWidth();
cy = ((float) y / 255)*getHeight();
} else {
cx = (1 - (float) x / 255)*getWidth();
cy = (1 - (float) y /255)*getHeight();
}
canvas.drawCircle(cx, cy, POINT_RADIUS, paint);
}
}
public void setFieldImage(FieldposType.FieldImage image) {
if(image.flippable && SettingsManager.getFieldImageFlipped()) {
setImageResource(image.resId_flipped);
flip = true;
} else {
setImageResource(image.resId_normal);
} }
} }
@@ -10,8 +10,6 @@ import android.widget.FrameLayout;
import android.widget.ImageView; import android.widget.ImageView;
import com.ridgebotics.ridgescout.R; import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.types.input.FieldposType;
import com.ridgebotics.ridgescout.utility.SettingsManager;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -50,6 +48,9 @@ public class MultiFieldPosView extends FrameLayout {
imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE); imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
imageView.setAdjustViewBounds(true); imageView.setAdjustViewBounds(true);
addView(imageView); addView(imageView);
setImageResource(R.drawable.field_2025);
} }
public void addPos(int[] pos){ public void addPos(int[] pos){
@@ -61,14 +62,6 @@ public class MultiFieldPosView extends FrameLayout {
invalidate(); invalidate();
} }
public void setFieldImage(FieldposType.FieldImage image) {
if(image.flippable && SettingsManager.getFieldImageFlipped()) {
setImageResource(image.resId_flipped);
} else {
setImageResource(image.resId_normal);
}
}
@Override @Override
protected void dispatchDraw(Canvas canvas) { protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas); super.dispatchDraw(canvas);
@@ -67,9 +67,9 @@ public class Colors {
public static final int fileselector_unselected_color = 0x50006600; public static final int fileselector_unselected_color = 0x50006600;
// TBA // TBA
public static final int tba_previous = 0x30FF0000; public static final int tba_previous = Color.argb(30, 64,64,64);
public static final int tba_current = 0x50ff0000; public static final int tba_current = 0x7f00ff00;
public static final int tba_next = 0x30FFFF00; public static final int tba_next = Color.argb(30, 192,192,192);
public static final int tba_red = 0x50ff0000; public static final int tba_red = 0x50ff0000;
public static final int tba_blue = 0x500000ff; public static final int tba_blue = 0x500000ff;
public static final int tba_toggle_background = 0x30000000; public static final int tba_toggle_background = 0x30000000;
@@ -17,7 +17,7 @@ public class SettingsManager {
public static final String SelEVCodeKey = "selected_event_code"; public static final String SelEVCodeKey = "selected_event_code";
public static final String YearNumKey = "year_num"; public static final String YearNumKey = "year_num";
public static final String FieldImageKey = "field_image_flipped"; public static final String FieldImageKey = "field_image";
public static final String MatchNumKey = "match_num"; public static final String MatchNumKey = "match_num";
public static final String AllyPosKey = "alliance_pos"; public static final String AllyPosKey = "alliance_pos";
@@ -46,8 +46,8 @@ public class SettingsManager {
hm.put(UnameKey, "Username"); hm.put(UnameKey, "Username");
hm.put(SelEVCodeKey, "unset"); hm.put(SelEVCodeKey, "unset");
hm.put(WifiModeKey, false); hm.put(WifiModeKey, false);
hm.put(YearNumKey, 2025); hm.put(YearNumKey, 2026);
hm.put(FieldImageKey, false); hm.put(FieldImageKey, "2026");
hm.put(MatchNumKey, 0); hm.put(MatchNumKey, 0);
hm.put(AllyPosKey, "red-1"); hm.put(AllyPosKey, "red-1");
hm.put(DataModeKey, 0); hm.put(DataModeKey, 0);
@@ -77,7 +77,7 @@ public class SettingsManager {
getEditor().putBoolean(WifiModeKey, (boolean) defaults.get( WifiModeKey )).apply(); getEditor().putBoolean(WifiModeKey, (boolean) defaults.get( WifiModeKey )).apply();
getEditor() .putInt(YearNumKey, (int) defaults.get( YearNumKey )).apply(); getEditor() .putInt(YearNumKey, (int) defaults.get( YearNumKey )).apply();
getEditor() .putBoolean(FieldImageKey, (boolean) defaults.get( FieldImageKey )).apply(); getEditor() .putString(FieldImageKey, (String) defaults.get( FieldImageKey )).apply();
getEditor() .putInt(MatchNumKey, (int) defaults.get( MatchNumKey )).apply(); getEditor() .putInt(MatchNumKey, (int) defaults.get( MatchNumKey )).apply();
getEditor() .putString(AllyPosKey, (String) defaults.get( AllyPosKey )).apply(); getEditor() .putString(AllyPosKey, (String) defaults.get( AllyPosKey )).apply();
getEditor() .putInt(DataModeKey, (int) defaults.get( DataModeKey )).apply(); getEditor() .putInt(DataModeKey, (int) defaults.get( DataModeKey )).apply();
@@ -108,8 +108,8 @@ public class SettingsManager {
public static int getYearNum(){return prefs.getInt( YearNumKey, (int) defaults.get(YearNumKey));} public static int getYearNum(){return prefs.getInt( YearNumKey, (int) defaults.get(YearNumKey));}
public static void setYearNum(int num){ getEditor().putInt( YearNumKey,num).apply();} public static void setYearNum(int num){ getEditor().putInt( YearNumKey,num).apply();}
public static boolean getFieldImageFlipped(){return prefs.getBoolean(FieldImageKey, (boolean) defaults.get(FieldImageKey));} public static String getFieldImageIndex(){return prefs.getString( FieldImageKey, (String) defaults.get(FieldImageKey));}
public static void setFieldImageFlipped(boolean bool){ getEditor().putBoolean(FieldImageKey, bool).apply();} public static void setFieldImageIndex(String str){ getEditor().putString( FieldImageKey,str).apply();}
public static int getMatchNum(){return prefs.getInt( MatchNumKey, (int) defaults.get(MatchNumKey));} public static int getMatchNum(){return prefs.getInt( MatchNumKey, (int) defaults.get(MatchNumKey));}
public static void setMatchNum(int num){ getEditor().putInt( MatchNumKey,num).apply();} public static void setMatchNum(int num){ getEditor().putInt( MatchNumKey,num).apply();}
BIN
View File
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 636 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 954 KiB

@@ -60,7 +60,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Pit Data" android:text="Pit Data"
android:textAlignment="center" android:textAlignment="center"
android:textSize="24sp" android:textSize="35sp"
app:layout_constraintTop_toBottomOf="@+id/team_description2" app:layout_constraintTop_toBottomOf="@+id/team_description2"
tools:layout_editor_absoluteX="0dp" /> tools:layout_editor_absoluteX="0dp" />
@@ -73,6 +73,11 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
<View
android:id="@+id/divider3"
android:layout_width="match_parent"
android:layout_height="12dp"
android:background="?android:attr/listDivider" />
</LinearLayout> </LinearLayout>
<TextView <TextView
@@ -81,7 +86,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Match Data" android:text="Match Data"
android:textAlignment="center" android:textAlignment="center"
android:textSize="24sp" android:textSize="35sp"
app:layout_constraintTop_toBottomOf="@+id/team_description2" app:layout_constraintTop_toBottomOf="@+id/team_description2"
tools:layout_editor_absoluteX="0dp" /> tools:layout_editor_absoluteX="0dp" />
@@ -139,9 +144,13 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
<View
android:id="@+id/divider2"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/listDivider" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>
+1 -1
View File
@@ -1,5 +1,5 @@
[versions] [versions]
agp = "8.13.0" agp = "8.13.2"
junit = "4.13.2" junit = "4.13.2"
junitVersion = "1.1.5" junitVersion = "1.1.5"
espressoCore = "3.5.1" espressoCore = "3.5.1"
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

After

Width:  |  Height:  |  Size: 493 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 KiB

After

Width:  |  Height:  |  Size: 434 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 KiB

After

Width:  |  Height:  |  Size: 454 KiB

-1
View File
@@ -17,7 +17,6 @@ dependencyResolutionManagement {
google() google()
mavenCentral() mavenCentral()
maven ( url = "https://jitpack.io" ) maven ( url = "https://jitpack.io" )
jcenter()
} }
} }