1 Commits

Author SHA1 Message Date
Daniel Carta b41ed2667c Testing 9/18 2025-09-18 17:51:05 -06:00
81 changed files with 1869 additions and 2309 deletions
Vendored
BIN
View File
Binary file not shown.
+7 -5
View File
@@ -11,10 +11,10 @@
#### 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.
- 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 losing any data!
- Similar to ScoutingPASS, there are many diffrent types of fields that can be used to collect data.
- The app is designed to handle updates to the fields on the fly, without loosing 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.
- Dynamic displays based on the different fields.
- Dynamic displays based off of the diffrent fields.
- Data transfer including 2D codes, Bluetooth, and File Bundle.
- Exporting using CSV.
- Deployment on F-Droid
@@ -22,13 +22,15 @@
#### Things that are yet to be implemented:
- A page that lets users cross-compare scouting data between teams. (Compare)
- A page that lets scouters more easily make reports to the drive team before a match starts (Report)
#### Things that may or may not be implemented:
- Statbotics integration
- Statbotics intgration
- Scout error estimation using OPR-like calculation
- - Would most likely require Statbotics
https://www.thebluealliance.com/avatars
### Screenshots
|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)|
BIN
View File
Binary file not shown.
+9 -9
View File
@@ -16,7 +16,7 @@ android {
dependenciesInfo {
// Disables dependency metadata when building APKs.
includeInApk = false
// Disables dependency metadata when building Android App Bundles.5
// Disables dependency metadata when building Android App Bundles.
includeInBundle = false
}
@@ -25,8 +25,8 @@ android {
applicationId = "com.ridgebotics.ridgescout"
minSdk = 24
targetSdk = 34
versionCode = 15 // **IMPORTANT** Increment this before releasing on github
versionName = "3.1"// **IMPORTANT** Change this before releasing on github (<Year num since 2024>.<Update Version>)
versionCode = 11 // **IMPORTANT** Increment this before releasing on github
versionName = "1.4"// **IMPORTANT** Change this before releasing on github (<Year num since 2024>.<Update Version>)
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
@@ -44,8 +44,8 @@ android {
buildFeatures {
viewBinding = true
}
androidResources {
noCompress += listOf("tflite")
aaptOptions {
noCompress("tflite");
}
}
@@ -66,10 +66,10 @@ dependencies {
androidTestImplementation(libs.espresso.core)
var camerax_version = "1.4.2"
implementation("androidx.camera:camera-core:${camerax_version}")
implementation("androidx.camera:camera-camera2:${camerax_version}")
implementation("androidx.camera:camera-lifecycle:${camerax_version}")
var camerax_version = "1.3.2"
implementation("androidx.camera:camera-core:1.3.2")
implementation("androidx.camera:camera-camera2:1.3.2")
implementation("androidx.camera:camera-lifecycle:1.3.2")
implementation("androidx.camera:camera-view:${camerax_version}")
implementation("com.journeyapps:zxing-android-embedded:4.3.0")
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
@@ -47,13 +47,11 @@ public class MainActivity extends AppCompatActivity {
// Load default match fields
if(!FileEditor.fileExist(Fields.matchFieldsFilename)){
Fields.save(Fields.matchFieldsFilename, Fields.default_match_fields);
FileEditor.toTheArchaicPeriod(Fields.matchFieldsFilename);
}
// Load default pit fields
if(!FileEditor.fileExist(Fields.pitsFieldsFilename)){
Fields.save(Fields.pitsFieldsFilename, Fields.default_pit_fields);
FileEditor.toTheArchaicPeriod(Fields.pitsFieldsFilename);
}
// get time zone for FTP file transfer
@@ -30,7 +30,36 @@ public class Fields {
public static final FieldType[][] default_match_fields = new FieldType[][] {
{
new FieldposType(uuid(),"Auto start pos", "Where does the robot start its auto?", 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 L3 Coral", "How many coral did this robot score in L3 during auto?", 0),
new TallyType(uuid(),"Auto L2 Coral", "How many coral did this robot score in L2 during auto?", 0),
new TallyType(uuid(),"Auto L1/Trough Coral", "How many coral did this robot score in L1 during auto?", 0),
new TallyType(uuid(),"Auto Processor Algae", "How many algae did this robot score in the Barge during auto?", 0),
new TallyType(uuid(),"Auto Barge Algae", "How many algae did this robot score in the Barge during auto?", 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 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 auto?", 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 auto?", 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 auto?", 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 DropdownType(uuid(),"Teleop Quality", "How did the robot drive during Teleop?", new String[]{"Smooth", "Jittery"}, 0),
new TextType(uuid(),"Teleop Comments", "Anything interesting about Teleop", ""),
new DropdownType(uuid(),"Climb State", "What was the final condition of the robot?", new String[]{"Nothing", "Continued Cycling", "Park", "Attempted Shallow", "Shallow", "Attempted Deep", "Deep"}, 0),
new DropdownType(uuid(),"Robot Condition", "Was anything broken?", new String[]{"Everything was working", "Something was maybe broken", "Something was broken", "Robot was disabled for part of the match", "Missing robot"}, 0),
new TextType(uuid(),"Other Comments", "Any other comments you have", "")
}
};
@@ -40,6 +69,14 @@ public class Fields {
new DropdownType(uuid(),"Intake type", "What type of intake does this team have?", new String[]{"Ground only", "Player Station only", "Both", "Other, Info in comments"}, 0),
new DropdownType(uuid(),"Intake Consistency", "How consistent is the robot at intakeing?", new String[]{"Does not work", "Worked a few times during testing", "Works most of the time", "Fails sometimes", "Never fails"}, 0),
new DropdownType(uuid(),"Score Area", "What does this robot score?", new String[]{"Only Algae", "Mostly Algae", "Both", "Mostly Coral", "Only Coral"}, 0),
new CheckboxType(uuid(),"L4 Scoring", "Will the robot score in Layer 4?", 0),
new CheckboxType(uuid(),"L3 Scoring", "Will the robot score in Layer 3?", 0),
new CheckboxType(uuid(),"L2 Scoring", "Will the robot score in Layer 3?", 0),
new CheckboxType(uuid(),"L1/Trough Scoring", "Will the robot score in Layer 1?", 0),
new CheckboxType(uuid(),"Processor Scoring", "Will the robot score in the Processor?", 0),
new CheckboxType(uuid(),"Barge Scoring", "Will the robot score algae in the Barge?", 0),
new DropdownType(uuid(),"Scoring Consistency", "How consistent is the robot at Scoring?", new String[]{"Does not work", "Worked a few times during testing", "Works most of the time", "Fails sometimes", "Never fails"}, 0),
new TextType(uuid(),"Auto Capability", "What autos does this team have?", ""),
@@ -60,57 +60,61 @@ public class ScoutingDataWriter {
public ScoutingArray data;
}
public static ParsedScoutingDataResult load(String filename, FieldType[][] values , TransferType[][] transferValues) throws BuiltByteParser.byteParsingExeption{
public static ParsedScoutingDataResult load(String filename, FieldType[][] values , TransferType[][] transferValues){
byte[] bytes = FileEditor.readFile(filename);
BuiltByteParser bbp = new BuiltByteParser(bytes);
// try {
ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse();
RawDataType[] rawDataTypes = new RawDataType[objects.size()-2];
try {
ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse();
RawDataType[] rawDataTypes = new RawDataType[objects.size()-2];
int version = ((int)objects.get(0).get());
int version = ((int)objects.get(0).get());
if(values.length <= version) {
if(values.length <= version) {
// AlertManager.addSimpleError("Error loading " + filename);
throw new BuiltByteParser.byteParsingExeption("Field version (" +version + ") is too recent as compared to latest version (" + (values.length-1) + ")!");
}
AlertManager.error(new BuiltByteParser.byteParsingExeption("Field version (" +version + ") is too recent as compared to latest version (" + (values.length-1) + ")!"));
return null;
}
// System.out.println(version);
String username = (String) objects.get(1).get();
String username = (String) objects.get(1).get();
for(int i = 0; i < values[version].length; i++){
switch (objects.get(i+2).getType()){
case 1: // Int
rawDataTypes[i] = IntType.newNull(values[version][i].UUID);
rawDataTypes[i].forceSetValue(objects.get(i+2).get());
Log.i(ParsedScoutingDataResult.class.toString(),"Loaded INT: " + values[version][i].name + " (" + values[version][i].UUID + ") " + ", ("+ rawDataTypes[i].get() +")");
break;
case 2: // String
rawDataTypes[i] = StringType.newNull(values[version][i].UUID);
rawDataTypes[i].forceSetValue(objects.get(i+2).get());
Log.i(ParsedScoutingDataResult.class.toString(),"Loaded STR: " + values[version][i].name + " (" + values[version][i].UUID + ") " + ", ("+ rawDataTypes[i].get() +")");
break;
case 3: // Int array
rawDataTypes[i] = IntArrType.newNull(values[version][i].UUID);
rawDataTypes[i].forceSetValue(objects.get(i+2).get());
Log.i(ParsedScoutingDataResult.class.toString(),"Loaded intARR: " + values[version][i].name + " (" + values[version][i].UUID + ") " + ", ("+ Arrays.toString((int[]) rawDataTypes[i].get()) +")");
break;
for(int i = 0; i < values[version].length; i++){
switch (objects.get(i+2).getType()){
case 1: // Int
rawDataTypes[i] = IntType.newNull(values[version][i].UUID);
rawDataTypes[i].forceSetValue(objects.get(i+2).get());
Log.i(ParsedScoutingDataResult.class.toString(),"Loaded INT: " + values[version][i].name + " (" + values[version][i].UUID + ") " + ", ("+ rawDataTypes[i].get() +")");
break;
case 2: // String
rawDataTypes[i] = StringType.newNull(values[version][i].UUID);
rawDataTypes[i].forceSetValue(objects.get(i+2).get());
Log.i(ParsedScoutingDataResult.class.toString(),"Loaded STR: " + values[version][i].name + " (" + values[version][i].UUID + ") " + ", ("+ rawDataTypes[i].get() +")");
break;
case 3: // Int array
rawDataTypes[i] = IntArrType.newNull(values[version][i].UUID);
rawDataTypes[i].forceSetValue(objects.get(i+2).get());
Log.i(ParsedScoutingDataResult.class.toString(),"Loaded intARR: " + values[version][i].name + " (" + values[version][i].UUID + ") " + ", ("+ Arrays.toString((int[]) rawDataTypes[i].get()) +")");
break;
}
}
ScoutingArray msa = new ScoutingArray(version, rawDataTypes, values, transferValues);
msa.update();
ParsedScoutingDataResult psda = new ParsedScoutingDataResult();
psda.filename = filename;
psda.username = username;
psda.version = version;
psda.data = msa;
return psda;
} catch (BuiltByteParser.byteParsingExeption e){
AlertManager.error(e);
return null;
}
ScoutingArray msa = new ScoutingArray(version, rawDataTypes, values, transferValues);
msa.update();
ParsedScoutingDataResult psda = new ParsedScoutingDataResult();
psda.filename = filename;
psda.username = username;
psda.version = version;
psda.data = msa;
return psda;
// }
}
// A function that takes in a list of names seperated by commas, and adds a name if it is not included
@@ -1,179 +0,0 @@
package com.ridgebotics.ridgescout.types;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder;
import com.ridgebotics.ridgescout.utility.FileEditor;
import java.io.File;
import java.time.Instant;
import java.time.temporal.TemporalField;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.function.Predicate;
public class ColabArray {
private enum Action {
ADD,
REMOVE
}
private List<Diff> changelog = new ArrayList<>();
private void addChange(Diff change) {
this.changelog.add(change);
}
private List<Diff> getChangelog() {
return changelog;
}
public void add(String item) {
Diff diff = new Diff();
diff.action = Action.ADD;
diff.content = item;
diff.time = new Date();
addChange(diff);
}
public void remove(String item) {
Diff diff = new Diff();
diff.action = Action.REMOVE;
diff.content = item;
diff.time = new Date();
addChange(diff);
}
public void remove(int index) {
remove(get().get(index));
}
private static class Diff {
public Action action;
public String content;
public Date time;
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj.getClass() != this.getClass()) {
return false;
}
Diff other = (Diff) obj;
return other.action == this.action &&
other.time.getTime() == this.time.getTime() &&
other.content.equals(this.content);
}
}
public byte[] encode() throws ByteBuilder.buildingException{
ByteBuilder bb = new ByteBuilder();
for(Diff change : this.changelog){
bb.addInt(change.action.ordinal());
bb.addString(change.content);
bb.addLong(change.time.getTime());
}
return bb.build();
}
public static ColabArray decode(byte[] bytes) throws BuiltByteParser.byteParsingExeption {
BuiltByteParser bbp = new BuiltByteParser(bytes);
List<BuiltByteParser.parsedObject> results = bbp.parse();
if(results.size() % 3 != 0){
throw new BuiltByteParser.byteParsingExeption("Wrong amount of elements in ColabArray!");
}
ColabArray arr = new ColabArray();
for(int i = 0; i < results.size(); i += 3) {
Diff diff = new Diff();
diff.action = Action.values()[(int) results.get(i).get()];
diff.content = (String) results.get(i+1).get();
diff.time = new Date((long) results.get(i+2).get());
arr.addChange(diff);
}
return arr;
}
public void append(ColabArray other) {
List<Diff> otherlog = other.getChangelog();
otherlog.removeIf(diff ->
this.changelog.contains(diff)
);
this.changelog.addAll(otherlog);
this.changelog = Arrays.asList(sort(this.changelog));
}
public void append(File other) {
byte[] bytes = FileEditor.readFile(other);
if(bytes == null) return;
try {
append(decode(bytes));
} catch (BuiltByteParser.byteParsingExeption e) {
AlertManager.error("Failed to append ColabArray!", e);
}
}
private static Diff[] sort(List<Diff> changelog) {
Diff[] sorted = changelog.toArray(new Diff[0]);
try {
Arrays.sort(sorted, (o1, o2) -> (int) (o1.time.getTime() - o2.time.getTime()));
} catch (Exception e){
AlertManager.error(e);
}
return sorted;
}
public List<String> get() {
List<String> result = new ArrayList<>();
for(Diff change : changelog) {
switch (change.action) {
case ADD:
result.add(change.content);
break;
case REMOVE:
result.remove(change.content);
break;
}
}
return result;
}
public boolean contains(String item) {
// Diff[] sorted = sort();
for(int i = changelog.size()-1; i >= 0; i--) {
Diff change = changelog.get(i);
if(!change.content.equals(item)) continue;
return change.action == Action.ADD;
}
return false;
}
public int size() {
return get().size();
}
}
@@ -41,6 +41,7 @@ public class ScoutingArray {
continue;
case CREATE:
new_values[i] = create_transfer((CreateTransferType) tv);
continue;
}
}
this.array = new_values;
@@ -71,6 +72,12 @@ public class ScoutingArray {
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){
FieldType it = get_input_type_by_UUID(version+1, tv.UUID);
switch (it.getValueType()){
@@ -32,6 +32,12 @@ public class ScoutingFile {
ByteBuilder bb = new ByteBuilder()
.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)));
return bb.build();
@@ -9,7 +9,6 @@ import com.ridgebotics.ridgescout.utility.ByteBuilder;
import com.ridgebotics.ridgescout.utility.SettingsManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
@@ -17,13 +16,12 @@ import java.util.stream.IntStream;
// Easily encoded and decoded to binary format.
public class frcEvent {
// public static final int typecode = 254; Unused, no idea what this is
public String eventCode; //Current event code
public static final int typecode = 254;
public String eventCode;
public String name;
public ArrayList<frcMatch> matches;
public ArrayList<frcTeam> teams;
// Turns frcEvent into raw data
public byte[] encode() {
try {
ByteBuilder bb = new ByteBuilder()
@@ -49,7 +47,6 @@ public class frcEvent {
}
}
//Decodes the frcEvent
public static frcEvent decode(byte[] bytes) {
try {
ArrayList<BuiltByteParser.parsedObject> objects =
@@ -78,7 +75,6 @@ public class frcEvent {
}
}
//Generates text
@NonNull
public String toString() {
return (
@@ -138,29 +134,29 @@ public class frcEvent {
return null;
}
// public
// Returns the soonest match that there will be all the possible upcoming data on other teams
public int getMostInformedBy(int ourTeamNum, int curMatch){
frcMatch teamMatch = getNextTeamMatch(ourTeamNum, curMatch);
public void getReportMatches(int ourTeamNum){
frcMatch[] teamMatches = event.getTeamMatches(ourTeamNum);
int maxMatch = Integer.MIN_VALUE;
for(int i = 0; i < teamMatches.length; i++){
int maxMatch = -1;
for(int a = 0; a < 6; a++){
int teamNum;
if(a < 3)
teamNum = teamMatches[i].redAlliance[a];
else
teamNum = teamMatches[i].blueAlliance[a-3];
for(int a = 0; a < 6; a++){
int teamNum;
if(a < 3)
teamNum = teamMatch.redAlliance[a];
else
teamNum = teamMatch.blueAlliance[a-3];
if(teamNum == ourTeamNum)
continue;
if(teamNum == ourTeamNum)
continue;
int matchNum = event.getMostRecentTeamMatch(teamNum, teamMatch.matchIndex);
if(maxMatch < matchNum)
maxMatch = matchNum;
int matchNum = event.getMostRecentTeamMatch(teamNum, teamMatches[i].matchIndex);
if(maxMatch < matchNum)
maxMatch = matchNum;
}
}
return maxMatch;
}
public frcTeam getTeamByNum(int teamNum){
@@ -172,26 +168,6 @@ public class frcEvent {
return null;
}
public List<frcTeam> getTeamsSorted() {
int[] teamNums = new int[teams.size()];
for(int i = 0 ; i < teams.size(); i++){
teamNums[i] = teams.get(i).teamNumber;
}
Arrays.sort(teamNums);
List<frcTeam> list = new ArrayList<>();
for(int i = 0; i < teams.size(); i++){
frcTeam team = getTeamByNum(teamNums[i]);
assert team != null;
list.add(team);
}
return list;
}
public boolean getIsBlueAlliance(int teamNum, int matchNum){
return getIsBlueAlliance(teamNum, matches.get(matchNum));
@@ -207,7 +207,6 @@ public class CheckboxType extends FieldType {
parent.addView(chart);
}
//TODO
public void addDataToTable(TableLayout parent, Map<Integer, List<RawDataType>> data){
}
@@ -29,7 +29,6 @@ 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.ridgebotics.ridgescout.utility.builders.TextViewBuilder;
import java.util.ArrayList;
import java.util.Arrays;
@@ -103,17 +102,19 @@ public class DropdownType extends FieldType {
// Dropdown view
public void add_individual_view(LinearLayout parent, RawDataType data){
if(data.isNull()) return;
parent.addView(
new TextViewBuilder(parent.getContext(), text_options[(int) data.get()])
.layout_match_wrap()
.padding(20)
.size(18)
.align_left()
.build());
TextView tv = new TextView(parent.getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setPadding(20,20,20,20);
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText(text_options[(int) data.get()]);
tv.setTextSize(18);
parent.addView(tv);
}
@@ -122,7 +123,7 @@ public class DropdownType extends FieldType {
// Generates N amount of colors, all opposite colors
private static int[] generateEquidistantColors(int N) {
int[] colors = new int[N];
float[] hsv = new float[3]; // Hue, Saturation, Value
@@ -138,7 +139,6 @@ public class DropdownType extends FieldType {
return colors;
}
// Turns the dropdown into a pie chart in the compiled view
public void add_compiled_view(LinearLayout parent, RawDataType[] data){
PieChart chart = new PieChart(parent.getContext());
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(
@@ -172,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){
LineChart chart = new LineChart(parent.getContext());
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(
@@ -239,7 +239,6 @@ public class DropdownType extends FieldType {
parent.addView(chart);
}
//TODO
public void addDataToTable(TableLayout parent, Map<Integer, List<RawDataType>> data){
}
@@ -24,6 +24,7 @@ public abstract class FieldType {
public static final int numberType = 251;
public static final int checkboxType = 250;
public static final int fieldposType = 249;
public static final int toggleType = 248;
public enum inputTypes {
SLIDER,
@@ -32,7 +33,8 @@ public abstract class FieldType {
TALLY,
NUMBER,
CHECKBOX,
FIELDPOS
FIELDPOS,
TOGGLE;
}
public String UUID;
public String name;
@@ -89,8 +91,6 @@ public abstract class FieldType {
public abstract void setViewValue(Object value);
public abstract RawDataType getViewValue();
public abstract void add_individual_view(LinearLayout parent, RawDataType data);
public abstract void add_compiled_view(LinearLayout parent, RawDataType[] data);
public abstract void add_history_view(LinearLayout parent, RawDataType[] data);
@@ -221,7 +221,6 @@ public class FieldposType extends FieldType {
parent.addView(chart);
}
//TODO
public void addDataToTable(TableLayout parent, Map<Integer, List<RawDataType>> data){
}
@@ -29,7 +29,6 @@ import com.ridgebotics.ridgescout.types.data.RawDataType;
import com.ridgebotics.ridgescout.types.data.IntType;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder;
import com.ridgebotics.ridgescout.utility.builders.TextViewBuilder;
import java.util.ArrayList;
import java.util.List;
@@ -119,11 +118,16 @@ public class NumberType extends FieldType {
public void add_individual_view(LinearLayout parent, RawDataType data){
if(data.isNull()) return;
parent.addView(new TextViewBuilder(parent.getContext(), String.valueOf((int) data.get()))
.layout_match_wrap()
.align_left()
.size(24)
.build());
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()));
tv.setTextSize(24);
parent.addView(tv);
}
@@ -312,7 +316,6 @@ public class NumberType extends FieldType {
parent.addView(chart);
}
//TODO
public void addDataToTable(TableLayout parent, Map<Integer, List<RawDataType>> data){
}
@@ -29,7 +29,6 @@ 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.ridgebotics.ridgescout.utility.builders.TextViewBuilder;
import java.util.ArrayList;
import java.util.Collections;
@@ -66,6 +65,7 @@ public class TallyType extends FieldType {
public TallyCounterView tally = null;
public View createView(Context context, Function<RawDataType, Integer> onUpdate){
tally = new TallyCounterView(context);
tally.setOnCountChangedListener(n -> onUpdate.apply(getViewValue()));
@@ -104,11 +104,16 @@ public class TallyType extends FieldType {
public void add_individual_view(LinearLayout parent, RawDataType data){
if(data.isNull()) return;
parent.addView(new TextViewBuilder(parent.getContext(), String.valueOf((int) data.get()))
.layout_match_wrap()
.align_left()
.size(24)
.build());
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()));
tv.setTextSize(24);
parent.addView(tv);
}
@@ -340,12 +345,16 @@ public class TallyType extends FieldType {
row = new TableRow(parent.getContext());
CandlestickView view = views.get(i);
row.addView(new TextViewBuilder(parent.getContext(), String.valueOf(view.teamNum))
.align_center()
.padding(10)
.h6()
.build());
TextView teamNum = new TextView(parent.getContext());
TableRow.LayoutParams params = new TableRow.LayoutParams();
params.gravity = Gravity.CENTER;
teamNum.setLayoutParams(params);
teamNum.setPadding(10,10,10,10);
teamNum.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Headline6);
teamNum.setText(String.valueOf(view.teamNum));
row.addView(teamNum);
row.addView(view);
parent.addView(row);
@@ -28,7 +28,6 @@ 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.ridgebotics.ridgescout.utility.builders.TextViewBuilder;
import java.util.ArrayList;
import java.util.List;
@@ -113,11 +112,15 @@ public class TextType extends FieldType {
public void add_individual_view(LinearLayout parent, RawDataType data){
if(data.isNull()) return;
parent.addView(new TextViewBuilder(parent.getContext(), (String) data.get())
.layout_match_wrap()
.align_left()
.size(18)
.build());
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) data.get());
tv.setTextSize(18);
parent.addView(tv);
}
@@ -141,20 +144,25 @@ public class TextType extends FieldType {
positive_mean = 0;
count = 0;
positive_text = new TextViewBuilder(parent.getContext())
.align_center()
.size(20)
.build();
positive_text = new TextView(parent.getContext());
positive_text.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
positive_text.setGravity(Gravity.CENTER_HORIZONTAL);
positive_text.setTextSize(20);
parent.addView(positive_text);
for (int i = 0; i < data.length; i++){
if (!data[i].isNull()) {
SentimentAnalysis.analyse((String) data[i].get(), sentiment -> {
positive_mean += sentiment;
count++;
SentimentAnalysis.analyse((String) data[i].get(), new SentimentAnalysis.resultCallback() {
@Override
public void onFinish(float sentiment) {
positive_mean += sentiment;
count++;
positive_text.setText("Sentiment: " + (positive_mean / count));
positive_text.setText("Sentiment: " + (positive_mean / count));
}
});
}
}
@@ -220,7 +228,6 @@ public class TextType extends FieldType {
}
//TODO
public void addDataToTable(TableLayout parent, Map<Integer, List<RawDataType>> data){
}
@@ -0,0 +1,368 @@
package com.ridgebotics.ridgescout.types.input;
import static com.ridgebotics.ridgescout.utility.Colors.chart_background;
import static com.ridgebotics.ridgescout.utility.Colors.chart_text;
import static com.ridgebotics.ridgescout.utility.Colors.dropdown_value_text_1;
import static com.ridgebotics.ridgescout.utility.Colors.dropdown_value_text_2;
import static com.ridgebotics.ridgescout.utility.Colors.tally_data;
import android.content.Context;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
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.ridgebotics.ridgescout.types.data.IntType;
import com.ridgebotics.ridgescout.types.data.RawDataType;
import com.ridgebotics.ridgescout.ui.data.DataProcessing;
import com.ridgebotics.ridgescout.ui.views.CandlestickHeader;
import com.ridgebotics.ridgescout.ui.views.CandlestickView;
import com.ridgebotics.ridgescout.ui.views.TallyCounterView;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
public class ToggleType extends FieldType {
public int get_byte_id() {return toggleType;}
public inputTypes getInputType(){return inputTypes.TOGGLE;}
public RawDataType.valueTypes getValueType(){return RawDataType.valueTypes.NUM;}
public Object get_fallback_value(){return 0;}
public ToggleType(){}
public String get_type_name(){return "Toggle";}
public ToggleType(String UUID, String name, String description, int default_value){
super(UUID, name, description);
this.default_value = default_value;
}
public void encodeData(ByteBuilder bb) throws ByteBuilder.buildingException {
bb.addInt((int) default_value);
}
public void decodeData(ArrayList<BuiltByteParser.parsedObject> objects) {
default_value = objects.get(0).get();
}
public TallyCounterView tally = null;
public View createView(Context context, Function<RawDataType, 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;
if(IntType.isNull((int)value)){
nullify();
return;
}
isBlank = false;
tally.setVisibility(View.VISIBLE);
tally.setValue((int) value);
}
public void nullify(){
isBlank = true;
tally.setVisibility(View.GONE);
}
public RawDataType getViewValue(){
if(tally == null) return null;
if(tally.getVisibility() == View.GONE) return IntType.newNull(name);
return new IntType(name, tally.getValue());
}
public void add_individual_view(LinearLayout parent, RawDataType data){
if(data.isNull()) 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()));
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 += (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){
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(data[i] != null && data[i].isNull())
values[(int) data[i].get()-min]++;
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());
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(tally_data);
dataSet.setValueTextColor(dropdown_value_text_1);
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(dropdown_value_text_2);
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(dropdown_value_text_2);
chart.getXAxis().setTextColor(chart_text);
chart.getAxisLeft().setTextColor(chart_text);
chart.getAxisRight().setTextColor(chart_text);
Legend legend = chart.getLegend();
legend.setTextColor(chart_text);
parent.addView(chart);
}
public void add_history_view(LinearLayout parent, RawDataType[] 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(chart_background);
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;
if(data[i].isNull()) continue;
entries.add(new Entry(i, (float)(int) data[i].get()));
}
LineDataSet dataSet = new LineDataSet(entries, name);
dataSet.setColor(tally_data);
dataSet.setValueTextColor(dropdown_value_text_1);
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(dropdown_value_text_2);
chart.getXAxis().setTextColor(chart_text);
chart.getAxisLeft().setTextColor(chart_text);
chart.getAxisRight().setTextColor(chart_text);
Legend legend = chart.getLegend();
legend.setTextColor(chart_text);
chart.getAxisLeft().setAxisMinimum(min);
chart.getAxisLeft().setAxisMaximum(max);
chart.getAxisRight().setAxisMinimum(min);
chart.getAxisRight().setAxisMaximum(max);
parent.addView(chart);
}
public void addDataToTable(TableLayout parent, Map<Integer, List<RawDataType>> data){
int[] tmp_abs_bounds = DataProcessing.getNumberBounds(data);
int absmin = tmp_abs_bounds[0];
int absmax = tmp_abs_bounds[1];
//(int[]) teamData.get(i).get())[0];
// AlertManager.alert("Results","Min: " + min + " Max: " + max);
parent.removeAllViews();
List<CandlestickView> views = new ArrayList<>();
for(Integer teamNum : data.keySet()){
CandlestickView candlestickView = new CandlestickView(parent.getContext());
candlestickView.fromTeamData(data.get(teamNum), teamNum, absmin, absmax);
views.add(candlestickView);
}
TableRow row = new TableRow(parent.getContext());
// Make candlestick chart fill full width
parent.setColumnStretchable(1, true);
// Fill in top left cell
row.addView(new View(parent.getContext()));
CandlestickHeader header = new CandlestickHeader(parent.getContext());
header.setScale(absmin, absmax);
row.addView(header);
parent.addView(row);
// parent.addView(new );
try {
Collections.sort(views, (a, b) -> (int) ((b.average - a.average) * 50.f));
}catch (Exception e){}
for(int i = 0; i < views.size(); i++){
row = new TableRow(parent.getContext());
CandlestickView view = views.get(i);
TextView teamNum = new TextView(parent.getContext());
TableRow.LayoutParams params = new TableRow.LayoutParams();
params.gravity = Gravity.CENTER;
teamNum.setLayoutParams(params);
teamNum.setPadding(10,10,10,10);
teamNum.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Headline6);
teamNum.setText(String.valueOf(view.teamNum));
row.addView(teamNum);
row.addView(view);
parent.addView(row);
}
}
public String toString(RawDataType data){
return String.valueOf((int) data.get());
}
}
@@ -58,7 +58,7 @@ public class FieldDataFragment extends Fragment {
for (int teamIndex = 0; teamIndex < event.teams.size(); teamIndex++) {
int teamNum = event.teams.get(teamIndex).teamNumber;
List<String> filenames = new ArrayList<>(List.of(FileEditor.getMatchesByTeamNum(evcode, event.teams.get(teamIndex).teamNumber)));
filenames.removeAll(rescout_list.get());
filenames.removeAll(rescout_list);
ArrayList<RawDataType> teamData = new ArrayList<>();
@@ -10,9 +10,6 @@ import static com.ridgebotics.ridgescout.utility.DataManager.pit_latest_values;
import static com.ridgebotics.ridgescout.utility.DataManager.pit_transferValues;
import static com.ridgebotics.ridgescout.utility.DataManager.pit_values;
import android.content.Intent;
import android.graphics.Paint;
import android.net.Uri;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -26,7 +23,6 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.SettingsManager;
import com.ridgebotics.ridgescout.databinding.FragmentDataTeamsBinding;
import com.ridgebotics.ridgescout.scoutingData.ScoutingDataWriter;
@@ -34,7 +30,6 @@ import com.ridgebotics.ridgescout.types.data.RawDataType;
import com.ridgebotics.ridgescout.types.frcTeam;
import com.ridgebotics.ridgescout.utility.DataManager;
import com.ridgebotics.ridgescout.utility.FileEditor;
import com.ridgebotics.ridgescout.utility.builders.TextViewBuilder;
import java.util.ArrayList;
import java.util.List;
@@ -71,37 +66,38 @@ public class TeamsFragment extends Fragment {
loadTeam(index);
});
binding.tbaButton.setOnClickListener(v -> openWebPage(
"https://www.thebluealliance.com/team/"+team.teamNumber+"/"+SettingsManager.getYearNum()
));
binding.statboticsButton.setOnClickListener(v -> openWebPage(
"https://www.statbotics.io/team/"+team.teamNumber+"/"+SettingsManager.getYearNum()
));
loadTeam(SettingsManager.getTeamsDataMode());
return binding.getRoot();
}
public void openWebPage(String url) {
Uri webpage = Uri.parse(url);
Intent intent = new Intent(Intent.ACTION_VIEW, webpage);
// if (intent.resolveActivity(getActivity().getPackageManager()) != null) {
startActivity(intent);
// }
}
public void loadTeam(int mode) {
// LinearLayout ll = new LinearLayout(getContext());
// ll.setLayoutParams(new LinearLayout.LayoutParams(
// ViewGroup.LayoutParams.MATCH_PARENT,
// ViewGroup.LayoutParams.WRAP_CONTENT
// ));
// ll.setOrientation(LinearLayout.VERTICAL);
// binding.teamsArea.addView(ll);
binding.dataTeamCard.fromTeam(team);
// tv = new TextView(getContext());
// tv.setLayoutParams(new FrameLayout.LayoutParams(
// ViewGroup.LayoutParams.MATCH_PARENT,
// ViewGroup.LayoutParams.WRAP_CONTENT
// ));
// tv.setGravity(Gravity.CENTER_HORIZONTAL);
// tv.setText(team.getDescription());
// tv.setTextSize(16);
// ll.addView(tv);
try {add_pit_data(team);}catch(Exception e){AlertManager.error(e);}
try {add_match_data(team, mode);}catch(Exception e){AlertManager.error(e);}
}
public void add_pit_data(frcTeam team) throws BuiltByteParser.byteParsingExeption {
public void add_pit_data(frcTeam team){
binding.pitArea.removeAllViews();
final String filename = evcode+"-"+team.teamNumber+".pitscoutdata";
@@ -121,34 +117,49 @@ public class TeamsFragment extends Fragment {
// ll.addView(new MaterialDivider(getContext()));
if(!FileEditor.fileExist(filename)){
binding.pitArea.addView(new TextViewBuilder(getContext(), "No pit data has been collected!")
.layout_match_wrap().align_center().size(23).build());
TextView tv = new TextView(getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText("No pit data has been collected!");
tv.setTextSize(23);
binding.pitArea.addView(tv);
return;
}
ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(filename, pit_values, pit_transferValues);
binding.pitArea.addView(new TextViewBuilder(getContext(), "Pit scouting by " + psda.username)
.layout_match_wrap()
.padding(0, 20, 0, 5)
.align_center()
.size(30)
.build()
);
TextView 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("Pit scouting by " + psda.username);
tv.setTextSize(30);
binding.pitArea.addView(tv);
for (int a = 0; a < psda.data.array.length; a++) {
TextViewBuilder tvb = new TextViewBuilder(getContext(), pit_latest_values[a].name)
.align_center()
.layout_match_wrap()
.size(25);
tv = new TextView(getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText(pit_latest_values[a].name);
tv.setTextSize(25);
if(psda.data.array[a].isNull()){
tvb.tv.setBackgroundColor(toggletitle_unselected);
tvb.tv.setTextColor(toggletitle_black_background);
tv.setBackgroundColor(toggletitle_unselected);
tv.setTextColor(toggletitle_black_background);
}
binding.pitArea.addView(tvb.build());
binding.pitArea.addView(tv);
pit_latest_values[a].add_individual_view(binding.pitArea, psda.data.array[a]);
@@ -164,11 +175,15 @@ public class TeamsFragment extends Fragment {
String[] files = FileEditor.getMatchesByTeamNum(evcode, team.teamNumber);
if(files.length == 0){
binding.matchArea.addView(new TextViewBuilder(getContext(), "No match data has been collected!")
.layout_match_wrap()
.align_center()
.size(23)
.build());
TextView tv = new TextView(getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText("No match data has been collected!");
tv.setTextSize(23);
binding.matchArea.addView(tv);
return;
}
@@ -221,27 +236,33 @@ public class TeamsFragment extends Fragment {
ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(files[matchIndex], match_values, match_transferValues);
TextView title = new TextViewBuilder(getContext(),
"M" + (match_num) + " " + split[2] + "-" + split[3] + " by " + psda.username)
.align_center()
.size(30)
.padding(0, 0, 40, 5)
.build();
title.setPaintFlags(title.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
binding.matchArea.addView(title);
TextView tv = new TextView(getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setPadding(0, 40, 0, 5);
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText("M" + (match_num) + " " + split[2] + "-" + split[3] + " by " + psda.username);
tv.setTextSize(30);
binding.matchArea.addView(tv);
for (int i = 0; i < psda.data.array.length; i++) {
TextViewBuilder tv = new TextViewBuilder(getContext(), match_latest_values[i].name)
.align_left()
.size(25);
tv = new TextView(getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText(match_latest_values[i].name);
tv.setTextSize(25);
if (psda.data.array[i].isNull()) {
tv.tv.setBackgroundColor(toggletitle_unselected);
tv.tv.setTextColor(toggletitle_black_background);
tv.setBackgroundColor(toggletitle_unselected);
tv.setTextColor(toggletitle_black_background);
}
binding.matchArea.addView(tv.build());
binding.matchArea.addView(tv);
if(psda.data.array[i] != null)
@@ -273,15 +294,16 @@ public class TeamsFragment extends Fragment {
}
for(int i = 0; i < match_latest_values.length; i++){
binding.matchArea.addView(
new TextViewBuilder(getContext(), match_latest_values[i].name)
.align_center()
.padding(0, 0, 20, 5)
.size(30)
.build()
);
TextView 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(match_latest_values[i].name);
tv.setTextSize(30);
binding.matchArea.addView(tv);
if(data[i] != null)
match_latest_values[i].add_compiled_view(binding.matchArea, data[i]);
@@ -307,18 +329,19 @@ public class TeamsFragment extends Fragment {
}
for(int i = 0; i < match_latest_values.length; i++){
TextView tv = new TextViewBuilder(getContext(), match_latest_values[i].name)
.align_center()
.size(30)
.padding(0,0,20,5)
.build();
tv.setPaintFlags(tv.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
TextView 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(match_latest_values[i].name);
tv.setTextSize(30);
binding.matchArea.addView(tv);
if(data[i] != null)
match_latest_values[i].add_history_view(binding.matchArea, data[i]);
}
}
}
@@ -30,7 +30,6 @@ import com.ridgebotics.ridgescout.utility.FileEditor;
import com.ridgebotics.ridgescout.types.frcEvent;
import com.ridgebotics.ridgescout.types.frcMatch;
import com.ridgebotics.ridgescout.utility.SettingsManager;
import com.ridgebotics.ridgescout.utility.builders.TextViewBuilder;
import java.util.ArrayList;
import java.util.Arrays;
@@ -60,10 +59,11 @@ public class EventFragment extends Fragment {
add_match_scouting(event);
}
private void addTableText(TableRow tr, String textStr){
tr.addView(new TextViewBuilder(getContext(), textStr)
.align_center()
.size(18)
.build());
TextView text = new TextView(getContext());
text.setTextSize(18);
text.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); // Text align center
text.setText(textStr);
tr.addView(text);
}
public void add_pit_scouting(frcEvent event){
@@ -94,32 +94,33 @@ public class EventFragment extends Fragment {
tr = new TableRow(getContext());
}
TextViewBuilder text = new TextViewBuilder(getContext(), String.valueOf(num))
.size(18)
.align_center();
TextView text = new TextView(getContext());
text.setTextSize(18);
text.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
text.setText(String.valueOf(num));
final String filename = event.eventCode + "-" + num + ".pitscoutdata";
if(FileEditor.fileExist(filename)){
final boolean[] rescout = {DataManager.rescout_list.contains(filename)};
text.tv.setBackgroundColor(rescout[0] ? color_rescout : color_found);
text.setBackgroundColor(rescout[0] ? color_rescout : color_found);
text.tv.setOnLongClickListener(view -> {
text.setOnLongClickListener(view -> {
rescout[0] = !rescout[0];
if(rescout[0]) {
text.tv.setBackgroundColor(color_rescout);
text.setBackgroundColor(color_rescout);
DataManager.rescout_list.add(filename);
}else{
text.tv.setBackgroundColor(color_found);
text.setBackgroundColor(color_found);
DataManager.rescout_list.remove(filename);
}
DataManager.save_rescout_list();
return true;
});
}else{
text.tv.setBackgroundColor(color_not_found);
text.setBackgroundColor(color_not_found);
}
tr.addView(text.build());
tr.addView(text);
}
if(tr != null)
binding.teamsTable.addView(tr);
@@ -152,6 +153,10 @@ public class EventFragment extends Fragment {
addTableText(tr, String.valueOf(match.matchIndex));
//
for(int i=0;i<6;i++){
TextView text = new TextView(getContext());
text.setTextSize(18);
text.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
int team_num;
String alliance_position;
@@ -163,23 +168,20 @@ public class EventFragment extends Fragment {
alliance_position = "blue-"+(i-2);
}
TextViewBuilder text = new TextViewBuilder(getContext(), String.valueOf(team_num))
.size(18)
.align_center();
text.setText(String.valueOf(team_num));
final String filename = event.eventCode + "-" + match.matchIndex + "-" + alliance_position + "-" + team_num + ".matchscoutdata";
if(FileEditor.fileExist(filename)){
final boolean[] rescout = {DataManager.rescout_list.contains(filename)};
text.tv.setBackgroundColor(rescout[0] ? color_rescout : color_found);
text.setBackgroundColor(rescout[0] ? color_rescout : color_found);
text.tv.setOnLongClickListener(view -> {
text.setOnLongClickListener(view -> {
rescout[0] = !rescout[0];
if(rescout[0]) {
text.tv.setBackgroundColor(color_rescout);
text.setBackgroundColor(color_rescout);
DataManager.rescout_list.add(filename);
}else{
text.tv.setBackgroundColor(color_found);
text.setBackgroundColor(color_found);
DataManager.rescout_list.remove(filename);
}
DataManager.save_rescout_list();
@@ -187,9 +189,9 @@ public class EventFragment extends Fragment {
});
}else{
text.tv.setBackgroundColor(color_not_found);
text.setBackgroundColor(color_not_found);
}
tr.addView(text.build());
tr.addView(text);
}
binding.matchTable.addView(tr);
@@ -21,7 +21,6 @@ import androidx.fragment.app.Fragment;
import com.google.android.material.divider.MaterialDivider;
import com.ridgebotics.ridgescout.ui.views.ToggleTitleView;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.SettingsManager;
import com.ridgebotics.ridgescout.databinding.FragmentScoutingMatchBinding;
import com.ridgebotics.ridgescout.scoutingData.ScoutingDataWriter;
@@ -33,7 +32,6 @@ import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.AutoSaveManager;
import com.ridgebotics.ridgescout.utility.DataManager;
import com.ridgebotics.ridgescout.utility.FileEditor;
import com.ridgebotics.ridgescout.utility.builders.TextViewBuilder;
// Fragment for match scouting data editing.
public class MatchScoutingFragment extends Fragment {
@@ -60,12 +58,10 @@ public class MatchScoutingFragment extends Fragment {
binding.matchTeamCard.setVisibility(View.VISIBLE);
if(DataManager.match_values == null || DataManager.match_values.length == 0){
binding.MatchScoutArea.addView(
new TextViewBuilder(getContext(), "Failed to load fields.\nTry to either download or create match scouting fields.")
.align_center()
.build());
TextView tv = new TextView(getContext());
tv.setText("Failed to load fields.\nTry to either download or create match scouting fields.");
tv.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
binding.MatchScoutArea.addView(tv);
return binding.getRoot();
}
@@ -351,7 +347,7 @@ public class MatchScoutingFragment extends Fragment {
public void get_fields() throws BuiltByteParser.byteParsingExeption{
public void get_fields(){
ScoutingDataWriter.ParsedScoutingDataResult psdr = ScoutingDataWriter.load(filename, DataManager.match_values, DataManager.match_transferValues);
RawDataType[] types = psdr.data.array;
@@ -20,10 +20,8 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.google.android.material.divider.MaterialDivider;
import com.ridgebotics.ridgescout.ui.views.PitScoutingIndicator;
import com.ridgebotics.ridgescout.ui.views.ToggleTitleView;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.SettingsManager;
import com.ridgebotics.ridgescout.databinding.FragmentScoutingPitBinding;
import com.ridgebotics.ridgescout.scoutingData.ScoutingDataWriter;
@@ -78,6 +76,7 @@ public class PitScoutingFragment extends Fragment {
String fileUsernames = "";
ToggleTitleView[] titles;
AutoSaveManager asm = new AutoSaveManager(this::save, AUTO_SAVE_DELAY);
ArrayList<RawDataType> rawDataTypes;
@@ -100,7 +99,7 @@ public class PitScoutingFragment extends Fragment {
}
public void set_indicator_color(int color){
binding.pitIndicator.setColor(color);
binding.pitFileIndicator.setBackgroundColor(color);
}
public void update_asm(){
@@ -115,9 +114,10 @@ public class PitScoutingFragment extends Fragment {
public void loadTeam(){
// clear_fields();
binding.pitFileIndicator.setVisibility(View.VISIBLE);
binding.pitsTeamCard.setVisibility(View.VISIBLE);
binding.pitIndicator.setTeamNum(team.teamNumber);
binding.pitIndicator.setUsername(SettingsManager.getUsername());
binding.pitBarTeamNum.setText(String.valueOf(team.teamNumber));
binding.pitUsername.setText(SettingsManager.getUsername());
binding.pitsTeamCard.fromTeam(team);
filename = evcode + "-" + team.teamNumber + ".pitscoutdata";
@@ -146,7 +146,7 @@ public class PitScoutingFragment extends Fragment {
}
}
binding.pitIndicator.bringToFront();
binding.pitFileIndicator.bringToFront();
asm.start();
@@ -154,7 +154,7 @@ public class PitScoutingFragment extends Fragment {
private void enableRescoutButton(){
set_indicator_color(rescout ? rescout_color : saved_color);
binding.pitIndicator.setOnLongClickListener(v -> {
binding.pitFileIndicator.setOnLongClickListener(v -> {
rescout = !rescout;
if(rescout){
set_indicator_color(rescout_color);
@@ -171,7 +171,7 @@ public class PitScoutingFragment extends Fragment {
}
private void disableRescoutButton(){
binding.pitIndicator.setOnLongClickListener(null);
binding.pitFileIndicator.setOnLongClickListener(null);
}
@@ -225,7 +225,7 @@ public class PitScoutingFragment extends Fragment {
}
}
public void get_fields() throws BuiltByteParser.byteParsingExeption{
public void get_fields(){
ScoutingDataWriter.ParsedScoutingDataResult psdr = ScoutingDataWriter.load(filename, pit_values, pit_transferValues);
RawDataType[] types = psdr.data.array;
@@ -15,7 +15,6 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -28,7 +27,6 @@ import com.ridgebotics.ridgescout.utility.FileEditor;
import com.ridgebotics.ridgescout.utility.SettingsManager;
import com.ridgebotics.ridgescout.databinding.FragmentScoutingBinding;
import com.ridgebotics.ridgescout.utility.DataManager;
import com.ridgebotics.ridgescout.utility.builders.TextViewBuilder;
import java.util.ArrayList;
import java.util.Set;
@@ -91,7 +89,7 @@ public class ScoutingFragment extends Fragment {
binding.textMatchAlliance.setVisibility(View.GONE);
binding.textName.setVisibility(View.GONE);
binding.infoBox.setVisibility(View.GONE);
binding.textNextMatch.setVisibility(View.GONE);
binding.textRescoutIndicator.setVisibility(View.GONE);
binding.matchScoutingButton.setEnabled(false);
@@ -125,7 +123,20 @@ public class ScoutingFragment extends Fragment {
findNavController(this).navigate(R.id.action_navigation_scouting_to_navigation_scouting_event);
});
updateDashboard();
binding.textName.setText("Welcome, " + SettingsManager.getUsername() + "!");
int matchNum = SettingsManager.getMatchNum();
int nextMatch = -1;
try {
nextMatch = event.getNextTeamMatch(SettingsManager.getTeamNum(), matchNum).matchIndex;
} catch (Exception e){
AlertManager.error(e);
}
binding.textNextMatch.setText("Our next match: Match " + nextMatch);
binding.textMatchAlliance.setText("Match: " + (matchNum+1) + ", " + SettingsManager.getAllyPos());
binding.textRescoutIndicator.setText("Things to rescout: " + DataManager.rescout_list.size());
return binding.getRoot();
}
@@ -154,38 +165,4 @@ public class ScoutingFragment extends Fragment {
});
}
private void updateDashboard() {
binding.textName.setText("Welcome, " + SettingsManager.getUsername() + "!");
binding.textRescoutIndicator.setText("Things to rescout: " + DataManager.rescout_list.size());
if(event.matches.size() == 0) {
binding.textMatchAlliance.setText("No Matches!");
} else {
int teamNum = SettingsManager.getTeamNum();
int curMatchNum = SettingsManager.getMatchNum();
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(), "Our next match: Match " + nextMatch)
.body1()
.build());
int informedBy = event.getMostInformedBy(teamNum, curMatchNum);
binding.infoBox.addView(new TextViewBuilder(getContext(), "Most informed by: Match " + informedBy)
.body1()
.build());
}
}
}
@@ -18,8 +18,8 @@ import com.ridgebotics.ridgescout.types.input.NumberType;
import com.ridgebotics.ridgescout.types.input.SliderType;
import com.ridgebotics.ridgescout.types.input.TallyType;
import com.ridgebotics.ridgescout.types.input.TextType;
import com.ridgebotics.ridgescout.types.input.ToggleType;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.builders.TextViewBuilder;
import java.lang.reflect.Field;
import java.util.UUID;
@@ -30,7 +30,8 @@ public class FieldEditorHelper {
paramNumber,
paramString,
paramStringArray,
paramNumberArray
paramNumberArray,
paramBoolean
}
public static class parameterType {
@@ -65,6 +66,15 @@ public class FieldEditorHelper {
}
}
public static class paramBoolean extends parameterType{
public boolean val;
public paramBoolean(String name, boolean val){
this.name = name + " (Boolean)";
this.val = val;
this.id = parameterTypeEnum.paramBoolean;
}
}
// public static class paramNumberArray extends parameterType{
// public int[] val;
// public paramNumberArray(String name, int[] val){
@@ -114,6 +124,12 @@ public class FieldEditorHelper {
new paramNumber("Default Y", 0)
};
public static final parameterType[] defaultToggleParam = new parameterType[]{
new paramString("Name", "New Toggle"),
new paramString("Description", ""),
new paramBoolean("Default true or false",true)
};
private static parameterType[] getSliderParams(SliderType s){
return new parameterType[]{
@@ -174,6 +190,13 @@ public class FieldEditorHelper {
new paramNumber("Default Y", ((int[]) s.default_value)[1])
};
}
private static parameterType[] getToggleParams(ToggleType s){
return new parameterType[]{
new paramString("Name", s.name),
new paramString("Description", s.description),
new paramBoolean("Default true or false", (boolean) s.default_value)
};
}
@@ -225,6 +248,12 @@ public class FieldEditorHelper {
};
}
public static void setToggleParam(ToggleType s, parameterType[] types){
s.name = ((paramString) types[0]).val;
s.description = ((paramString) types[1]).val;
s.default_value = ((paramBoolean) types[2]).val;
}
private static void setInputParameter(FieldType t, parameterType[] types){
switch (t.getInputType()){
@@ -249,6 +278,9 @@ public class FieldEditorHelper {
case FIELDPOS:
setFieldPosParam((FieldposType) t, types);
break;
case TOGGLE:
setToggleParam((ToggleType) t, types);
break;
}
}
@@ -270,6 +302,8 @@ public class FieldEditorHelper {
return getCheckboxParam((CheckboxType) t);
case FIELDPOS:
return getFieldPosParam((FieldposType) t);
case TOGGLE:
return getToggleParams((ToggleType) t);
}
return new parameterType[]{};
}
@@ -306,6 +340,16 @@ public class FieldEditorHelper {
));
return text;
}
private static View createBooleanEdit(Context c, boolean value){
EditText text = new EditText(c);
text.setText(value);
text.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
return text;
}
private static View createEdit(Context c, parameterType t){
switch (t.id){
@@ -385,6 +429,11 @@ public class FieldEditorHelper {
fieldposType.UUID = UUID.randomUUID().toString();
setFieldPosParam(fieldposType, defaultFieldPosParam);
return fieldposType;
case 7:
ToggleType toggleType = new ToggleType();
toggleType.UUID = UUID.randomUUID().toString();
setToggleParam(toggleType, defaultToggleParam);
return toggleType;
}
return null;
}
@@ -398,11 +447,11 @@ public class FieldEditorHelper {
this.t = t;
views = new View[types.length];
for(int i = 0; i < types.length; i++){
parentView.addView(new TextViewBuilder(c, types[i].name)
.align_center()
.size(20)
.build());
TextView tv = new TextView(c);
tv.setText(types[i].name);
tv.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
tv.setTextSize(20);
parentView.addView(tv);
views[i] = createEdit(c, types[i]);
parentView.addView(views[i]);
@@ -21,7 +21,6 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation;
import com.google.android.material.button.MaterialButton;
import com.ridgebotics.ridgescout.MainActivity;
import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.databinding.FragmentSettingsFieldsBinding;
@@ -30,7 +29,6 @@ import com.ridgebotics.ridgescout.types.input.FieldType;
import com.ridgebotics.ridgescout.ui.views.CustomSpinnerView;
import com.ridgebotics.ridgescout.ui.views.FieldDisplay;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.builders.TextViewBuilder;
import java.util.ArrayList;
import java.util.Collections;
@@ -197,9 +195,10 @@ public class FieldsFragment extends Fragment {
sv.addView(table);
TextView UUID = new TextView(getContext());
UUID.setText("Type: " + field.get_type_name() + "\nUUID: " + field.UUID);
table.addView(new TextViewBuilder(getContext(), "Type: " + field.get_type_name() + "\nUUID: " + field.UUID)
.build());
table.addView(UUID);
FieldEditorHelper f = new FieldEditorHelper(getContext(), field, table);
@@ -217,7 +216,7 @@ public class FieldsFragment extends Fragment {
AlertDialog dialog = alert.create();
dialog.show();
MaterialButton deleteButton = new MaterialButton(getContext());
Button deleteButton = new Button(getContext());
deleteButton.setText("DELETE");
deleteButton.setOnClickListener(l -> {
AlertDialog.Builder alert2 = new AlertDialog.Builder(getContext());
@@ -1,7 +1,6 @@
package com.ridgebotics.ridgescout.ui.settings;
import static android.widget.LinearLayout.HORIZONTAL;
import static android.widget.LinearLayout.VERTICAL;
import static android.view.View.VISIBLE;
import static androidx.navigation.fragment.FragmentKt.findNavController;
import static com.ridgebotics.ridgescout.utility.SettingsManager.AllyPosKey;
import static com.ridgebotics.ridgescout.utility.SettingsManager.CustomEventsKey;
@@ -14,15 +13,11 @@ import static com.ridgebotics.ridgescout.utility.SettingsManager.UnameKey;
import static com.ridgebotics.ridgescout.utility.SettingsManager.WifiModeKey;
import static com.ridgebotics.ridgescout.utility.SettingsManager.YearNumKey;
import static com.ridgebotics.ridgescout.utility.SettingsManager.defaults;
import static com.ridgebotics.ridgescout.utility.SettingsManager.getEVCode;
import static com.ridgebotics.ridgescout.utility.SettingsManager.getEditor;
import static com.ridgebotics.ridgescout.utility.SettingsManager.prefs;
import android.app.AlertDialog;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.text.Editable;
import android.text.InputType;
@@ -31,6 +26,7 @@ import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ScrollView;
@@ -40,9 +36,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.checkbox.MaterialCheckBox;
import com.google.android.material.divider.MaterialDivider;
import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;
import com.ridgebotics.ridgescout.R;
@@ -50,11 +44,9 @@ import com.ridgebotics.ridgescout.databinding.FragmentSettingsBinding;
import com.ridgebotics.ridgescout.scoutingData.Fields;
import com.ridgebotics.ridgescout.ui.views.CustomSpinnerView;
import com.ridgebotics.ridgescout.ui.views.TallyCounterView;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.DataManager;
import com.ridgebotics.ridgescout.utility.FileEditor;
import com.ridgebotics.ridgescout.utility.ToDelete;
import com.ridgebotics.ridgescout.utility.builders.TextViewBuilder;
import com.ridgebotics.ridgescout.utility.SettingsManager;
import java.util.ArrayList;
import java.util.Arrays;
@@ -73,6 +65,20 @@ public class SettingsFragment extends Fragment {
reloadSettings();
binding.fieldsButton.setOnClickListener(v -> {
binding.fieldsButton.setEnabled(false);
binding.fieldsButtons.setVisibility(VISIBLE);
});
binding.fieldsMatchesButton.setOnClickListener(v -> {
FieldsFragment.set_filename(Fields.matchFieldsFilename);
findNavController(this).navigate(R.id.action_navigation_settings_to_navigation_data_fields);
});
binding.fieldsPitsButton.setOnClickListener(v -> {
FieldsFragment.set_filename(Fields.pitsFieldsFilename);
findNavController(this).navigate(R.id.action_navigation_settings_to_navigation_data_fields);
});
return root;
@@ -85,79 +91,28 @@ public class SettingsFragment extends Fragment {
SettingsManager manager = new SettingsManager(getContext());
ButtonSettingsItem appInfoButton = new ButtonSettingsItem();
appInfoButton.addButton("App info", v -> showAppInfo());
manager.addItem(appInfoButton);
ButtonSettingsItem corruptButton = new ButtonSettingsItem();
corruptButton.addButton("find corrupted files", view -> {
ToDelete.findCorruptedFiles(getContext());
});
corruptButton.addButton("delete files", view -> {
ToDelete.deleteFiles(getContext(), Arrays.asList(FileEditor.getFiles()), false);
});
// corruptButton.setEnabled(!getEVCode().equals("unset"));
manager.addItem(corruptButton);
manager.addItem(new HeaderSettingsItem("Advanced"));
ButtonSettingsItem fieldsButtons = new ButtonSettingsItem();
fieldsButtons.addButton("Edit pit fields", v -> {
FieldsFragment.set_filename(Fields.pitsFieldsFilename);
findNavController(this).navigate(R.id.action_navigation_settings_to_navigation_data_fields);
});
fieldsButtons.addButton("Edit match fields", v -> {
FieldsFragment.set_filename(Fields.matchFieldsFilename);
findNavController(this).navigate(R.id.action_navigation_settings_to_navigation_data_fields);
});
manager.addItem(fieldsButtons);
ButtonSettingsItem noticeButton = new ButtonSettingsItem();
noticeButton.addButton("Edit scout notice", v->editNotice());
noticeButton.setEnabled(!getEVCode().equals("unset"));
manager.addItem(noticeButton);
manager.addItem(new CheckboxSettingsItem(CustomEventsKey, "Custom Events"));
CheckboxSettingsItem FTPSendMetaFiles = new CheckboxSettingsItem(com.ridgebotics.ridgescout.utility.SettingsManager.FTPSendMetaFiles, "[⚠] Send meta files");
manager.addItem(FTPSendMetaFiles);
manager.addItem(new HeaderSettingsItem("Admin"));
StringSettingsItem FTPKey = new StringSettingsItem(com.ridgebotics.ridgescout.utility.SettingsManager.FTPKey, "Sync Key");
manager.addItem(FTPKey);
StringSettingsItem FTPServer = new StringSettingsItem(com.ridgebotics.ridgescout.utility.SettingsManager.FTPServer, "Sync Server (Sync)");
manager.addItem(FTPServer);
CheckboxSettingsItem FTPEnabled = new CheckboxSettingsItem(com.ridgebotics.ridgescout.utility.SettingsManager.FTPEnabled, "Sync Enabled", FTPServer, FTPKey, FTPSendMetaFiles);
CheckboxSettingsItem FTPSendMetaFiles = new CheckboxSettingsItem(com.ridgebotics.ridgescout.utility.SettingsManager.FTPSendMetaFiles, "⚠ Send meta files");
manager.addItem(FTPSendMetaFiles);
CheckboxSettingsItem FTPEnabled = new CheckboxSettingsItem(com.ridgebotics.ridgescout.utility.SettingsManager.FTPEnabled, "FTP Enabled", FTPServer, FTPKey, FTPSendMetaFiles);
manager.addItem(FTPEnabled);
manager.addItem(new CheckboxSettingsItem(WifiModeKey, "Wifi Mode", FTPEnabled));
manager.addItem(new NumberSettingsItem(YearNumKey, "Year", 0, 9999));
manager.addItem(new HeaderSettingsItem("Connection"));
manager.addItem(new CheckboxSettingsItem(EnableQuickAllianceChangeKey, "Enable quick alliance swap", null));
manager.addItem(new DropdownSettingsItem(FieldImageKey, "Field Image", new String[]{
"2026",
"2026 (Flipped)",
"2025",
"2025 (Flipped)"
}));
manager.addItem(new NumberSettingsItem(YearNumKey, "Year", 0, 9999));
manager.addItem(new DropdownSettingsItem(AllyPosKey, "Alliance Pos", alliance_pos_list));
int max = 0;
@@ -179,48 +134,16 @@ public class SettingsFragment extends Fragment {
manager.addItem(new StringSettingsItem(UnameKey, "Username"));
manager.addItem(new NumberSettingsItem(TeamNumKey, "Team Number", 0, 99999));
manager.addItem(new HeaderSettingsItem("Scouting"));
binding.SettingsTable.removeAllViews();
manager.getView(binding.SettingsTable);
// Add "Edit scout notice" button to the bottom of the page=
// Add "Field edit" buttons to the bottom of the page
// LinearLayout fieldButtons = new LinearLayout(getContext());
// fieldButtons.setLayoutParams(new LinearLayout.LayoutParams(
// LinearLayout.LayoutParams.MATCH_PARENT,
// LinearLayout.LayoutParams.WRAP_CONTENT
// ));
// fieldButtons.setOrientation(HORIZONTAL);
//
// Button editMatchFieldsButton = new Button(getContext());
// editMatchFieldsButton.setText("Edit Match Fields");
// editMatchFieldsButton.setOnClickListener(v -> {
// FieldsFragment.set_filename(Fields.matchFieldsFilename);
// findNavController(this).navigate(R.id.action_navigation_settings_to_navigation_data_fields);
// });
// fieldButtons.addView(editMatchFieldsButton);
//
//
// Button editPitsFieldsButton = new Button(getContext());
// editPitsFieldsButton.setText("Edit pits Fields");
// editPitsFieldsButton.setOnClickListener(v -> {
// FieldsFragment.set_filename(Fields.pitsFieldsFilename);
// findNavController(this).navigate(R.id.action_navigation_settings_to_navigation_data_fields);
// });
// fieldButtons.addView(editPitsFieldsButton);
// binding.SettingsTable.addView(fieldButtons);
if(!DataManager.getevcode().equals("unset")){
Button editNoticeButton = new Button(getContext());
editNoticeButton.setText("Edit Scout Notice");
binding.SettingsTable.addView(editNoticeButton);
editNoticeButton.setOnClickListener(v->editNotice());
}
}
@@ -255,41 +178,6 @@ public class SettingsFragment extends Fragment {
private TextView createText(String title) {
return new TextViewBuilder(getContext(), title)
.body1().build();
}
private void showAppInfo() {
LinearLayout ll = new LinearLayout(getContext());
ll.setOrientation(VERTICAL);
ll.setPadding(10, 10, 10, 10);
try {
PackageInfo pInfo = getContext().getPackageManager().getPackageInfo(getContext().getPackageName(), 0);
ll.addView(createText("Package: " + pInfo.packageName));
ll.addView(createText("Version: " + pInfo.versionName));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
ll.addView(createText("Signature: " + (pInfo.signingInfo != null ? "True" : "False")));
}
} catch (PackageManager.NameNotFoundException e) {
AlertManager.error("Failed to get version info", e);
}
AlertDialog.Builder alert = new AlertDialog.Builder(getContext());
alert.setTitle("App info");
alert.setView(ll);
alert.setNeutralButton("Ok", null);
alert.setCancelable(true);
alert.create().show();
}
@@ -396,8 +284,9 @@ public class SettingsFragment extends Fragment {
@Override
public View createView(Context context) {
TextView titleView = new TextViewBuilder(context, getTitle())
.sub1().build();
TextView titleView = new TextView(context);
titleView.setText(getTitle());
titleView.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Subtitle1);
TextInputLayout textInputLayout = new TextInputLayout(context);
editText = new TextInputEditText(context);
@@ -458,7 +347,7 @@ public class SettingsFragment extends Fragment {
@Override
public View createView(Context context) {
LinearLayout ll = new LinearLayout(getContext());
ll.setOrientation(VERTICAL);
ll.setOrientation(LinearLayout.VERTICAL);
tally = new TallyCounterView(getContext());
@@ -477,10 +366,11 @@ public class SettingsFragment extends Fragment {
});
tally.setEnabled(enabled);
ll.addView(new TextViewBuilder(getContext(), getTitle())
.h6()
.build());
TextView tv = new TextView(getContext());
tv.setText(getTitle());
tv.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Headline6);
tv.setGravity(Gravity.CENTER);
ll.addView(tv);
ll.addView(tally);
@@ -585,93 +475,6 @@ public class SettingsFragment extends Fragment {
}
}
public class HeaderSettingsItem extends SettingsItem<Void> {
String title;
public HeaderSettingsItem(String title) {
super("", title, null);
this.title = title;
}
@Override
public void setEnabled(boolean enabled){
}
@Override
public View createView(Context context) {
LinearLayout ll = new LinearLayout(context);
ll.setOrientation(VERTICAL);
ll.setPadding(0, 20,0,0);
ll.addView(new TextViewBuilder(context, title)
.h4()
.build());
ll.addView(new MaterialDivider(context));
return ll;
}
@Override
public Void getValue() {
return null;
}
}
public class ButtonSettingsItem extends SettingsItem<Void> {
List<MaterialButton> buttons = new ArrayList<>();
List<String> titles = new ArrayList<>();
List<View.OnClickListener> callbacks = new ArrayList<>();
boolean enabled = true;
public ButtonSettingsItem() {
super("", "", null);
}
@Override
public void setEnabled(boolean enabled){
this.enabled = enabled;
for(int i = 0; i < buttons.size(); i++){
buttons.get(i).setEnabled(enabled);
}
}
public void addButton(String text, View.OnClickListener onClickListener) {
titles.add(text);
callbacks.add(onClickListener);
}
@Override
public View createView(Context context) {
LinearLayout ll = new LinearLayout(context);
ll.setOrientation(HORIZONTAL);
for(int i = 0; i < titles.size(); i++){
MaterialButton button = new MaterialButton(context);
button.setText(titles.get(i));
button.setOnClickListener(callbacks.get(i));
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT, 1.0f);
layoutParams.setMargins(3, 0, 3, 0);
// layoutParams.weight
button.setLayoutParams(layoutParams);
button.setEnabled(enabled);
// button.weight
buttons.add(button);
ll.addView(button);
}
return ll;
}
@Override
public Void getValue() {
return null;
}
}
public class SettingsManager {
private Context context;
private HashMap<String, Object> settings;
@@ -694,7 +497,7 @@ public class SettingsFragment extends Fragment {
items.add(item);
LinearLayout itemContainer = new LinearLayout(context);
itemContainer.setOrientation(VERTICAL);
itemContainer.setOrientation(LinearLayout.VERTICAL);
itemContainer.setPadding(32, 0, 32, 8);
View view = item.createView(context);
@@ -0,0 +1,272 @@
package com.ridgebotics.ridgescout.ui.transfer;
import static com.ridgebotics.ridgescout.utility.DataManager.evcode;
import static com.ridgebotics.ridgescout.utility.FileEditor.baseDir;
import android.util.Log;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder;
import com.ridgebotics.ridgescout.utility.FileEditor;
import com.ridgebotics.ridgescout.utility.SettingsManager;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
// This is now deprecated
// Class to synchronise data over FTP.
public class FTPSync extends Thread {
public static final String remoteBasePath = "/RidgeScout/";
public static final String timestampsFilename = "timestamps";
public static long lastSyncTime = 0;
private static Date curSyncTime;
private static final long millisTolerance = 1000;
private boolean after(Date a, Date b){
return a.getTime() - b.getTime() > millisTolerance;
}
public interface onResult {
void onResult(boolean error, int upCount, int downCount);
}
public interface UpdateIndicator {
void onText(String text);
}
private static UpdateIndicator updateIndicator = text -> {};
public static String text = "";
private static void setUpdateIndicator(String m_text){
text = m_text;
updateIndicator.onText(m_text);
}
public static void setOnUpdateIndicator(UpdateIndicator m_updateIndicator){
updateIndicator = m_updateIndicator;
}
private static onResult onResult = (error, upCount, downCount) -> {};
public static void setOnResult(onResult result){
onResult = result;
}
private static boolean isRunning = false;
public static boolean getIsRunning(){return isRunning;}
public static void sync(){
// DataManager.reload_event();
FTPSync ftpSync = new FTPSync();
curSyncTime = new Date();
ftpSync.start();
}
FTPClient ftpClient;
private int upCount = 0;
private int downCount = 0;
private void downloadFile(String remoteFile, File localFile) throws IOException {
try (FileOutputStream fos = new FileOutputStream(localFile)) {
ftpClient.retrieveFile(remoteBasePath + remoteFile, fos);
}
}
private void uploadFile(File localFile) throws IOException {
try (FileInputStream fis = new FileInputStream(localFile)) {
ftpClient.storeFile(remoteBasePath + localFile.getName(), fis);
}
}
private FTPFile findRemoteFile(FTPFile[] remoteFiles, String fileName) {
for (FTPFile file : remoteFiles) {
if (file.getName().equals(fileName)) {
return file;
}
}
return null;
}
private Date getUtcTimestamp(FTPFile file) {
return file.getTimestamp().getTime();
}
private Date getLocalFileUtcTimestamp(File file) {
return new Date(file.lastModified());
}
private void setLocalFileTimestamp(File file, Date date) {
file.setLastModified(date.getTime());
}
public void run() {
isRunning = true;
boolean sendMetaFiles = SettingsManager.getFTPSendMetaFiles();
// Meta files
List<String> meta_string_array = Arrays.asList(
"matches.fields",
"pits.fields",
evcode+".eventdata"
);
try {
// Login to FTP
ftpClient = new FTPClient();
InetAddress address = InetAddress.getByName(SettingsManager.getFTPServer());
ftpClient.connect(address);
ftpClient.login("anonymous", null);
ftpClient.enterLocalPassiveMode();
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
File localDir = new File(baseDir);
File[] localFiles = localDir.listFiles();
Map<String, Date> remoteTimestamps = getTimestamps();
// Loop through local files and send all that are more recent
if (localFiles != null) {
for (int i = 0; i < localFiles.length; i++) {
File localFile = localFiles[i];
setUpdateIndicator("Uploading " + (i+1) + "/" + localFiles.length);
if(localFile.isDirectory()) continue;
// Remove timestamts file
if(localFile.getName().equals(timestampsFilename)) continue;
// Remove meta files if the option is disabled
if(!sendMetaFiles && meta_string_array.contains(localFile.getName())) continue;
Date remoteTimestamp = remoteTimestamps.get(localFile.getName());
Date localTimeStamp = getLocalFileUtcTimestamp(localFile);
if (remoteTimestamp == null || after(localTimeStamp, remoteTimestamp)) {
uploadFile(localFile);
Log.i(getClass().toString(), "Uploaded" + localFile.getName());
setLocalFileTimestamp(localFile, curSyncTime);
remoteTimestamps.put(localFile.getName(), curSyncTime);
upCount++;
}else{
Log.i(getClass().toString(), "Did not upload");
}
}
}
Set<String> keySet = remoteTimestamps.keySet();
Iterator<String> keyIt = keySet.iterator();
for (int i = 0; i < keySet.size(); i++) {
String remoteFile = keyIt.next();
setUpdateIndicator("Downloading " + (i+1) + "/" + keySet.size());
File localFile = new File(baseDir, remoteFile);
if(remoteFile.equals(timestampsFilename)) continue;
// Remove meta files if the option is disabled
if(!sendMetaFiles && meta_string_array.contains(remoteFile)) continue;
// Date t1 = getLocalFileUtcTimestamp(localFile);
// Date t2 = getUtcTimestamp(remoteFile);
////
// System.out.println("- " + t1 + (t1.after(t2) ? ">" : "<") + t2);
Date localTimeStamp = getLocalFileUtcTimestamp(localFile);
Date remoteTimestamp = remoteTimestamps.get(remoteFile);
if (!localFile.exists() || (after(remoteTimestamp, localTimeStamp) && !localTimeStamp.equals(remoteTimestamp))) {
downloadFile(remoteFile, localFile);
Log.i(getClass().toString(), "Downloaded " + localFile.getName());
if(!localFile.exists()) Log.i(getClass().toString(), "Not exist");
else if(after(remoteTimestamp, localTimeStamp)) Log.i(getClass().toString(), "Before: " + (localTimeStamp.getTime()-remoteTimestamp.getTime()));
// Date d = getUtcTimestamp(remoteFile);
setLocalFileTimestamp(localFile, remoteTimestamps.get(localFile.getName()));
// remoteTimestamps.put(remoteFile, curSyncTime);
downCount++;
}else{
Log.i(getClass().toString(), "Did not download");
}
}
setTimestamps(remoteTimestamps);
} catch (Exception e) {
AlertManager.error("Failed Syncing!", e);
onResult.onResult(true, upCount, downCount);
setUpdateIndicator("ERROR!");
} finally {
onResult.onResult(false, upCount, downCount);
setUpdateIndicator("Finished");
}
isRunning = false;
}
private boolean setTimestamps(Map<String, Date> timestamps){
try {
ByteBuilder bb = new ByteBuilder();
String[] filenames = timestamps.keySet().toArray(new String[0]);
for(int i = 0; i < filenames.length; i++){
bb.addString(filenames[i]);
bb.addLong(timestamps.get(filenames[i]).getTime());
}
FileEditor.writeFile(timestampsFilename, bb.build());
uploadFile(new File(baseDir + timestampsFilename));
return true;
} catch (ByteBuilder.buildingException | IOException e) {
AlertManager.error("Failed Syncing!", e);
return false;
}
}
private Map<String, Date> getTimestamps() {
try {
downloadFile(timestampsFilename, new File(baseDir + timestampsFilename));
byte[] data = FileEditor.readFile(timestampsFilename);
if(data == null || data.length == 0)
return new HashMap<>();
BuiltByteParser bbp = new BuiltByteParser(data);
List<BuiltByteParser.parsedObject> pa = bbp.parse();
Map<String, Date> output = new HashMap<>();
for(int i = 0; i < pa.size(); i+=2){
// System.out.println((long) pa.get(i).get());
output.put(
(String) pa.get(i).get(),
new Date((long) pa.get(i+1).get())
);
}
return output;
}catch (IOException | BuiltByteParser.byteParsingExeption e){
AlertManager.error("Failed Syncing!", e);
return new HashMap<>();
}
}
}
@@ -24,7 +24,6 @@ import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.ByteBuilder;
import com.ridgebotics.ridgescout.utility.DataManager;
import com.ridgebotics.ridgescout.utility.FileEditor;
import com.ridgebotics.ridgescout.utility.builders.TextViewBuilder;
import java.util.ArrayList;
import java.util.Arrays;
@@ -50,10 +49,7 @@ public class FileSelectorFragment extends Fragment {
meta_string_array = new String[]{
"matches.fields",
"pits.fields",
evcode+".eventdata",
evcode+".rescout",
evcode+".scoutnotice",
"todelete.colabarray",
evcode+".eventdata"
};
String[] files = FileEditor.getEventFiles(evcode);
@@ -78,12 +74,10 @@ public class FileSelectorFragment extends Fragment {
checkBox.setChecked(true);
tr.addView(checkBox);
// Filename
tr.addView(
new TextViewBuilder(getContext(), files[i])
.size(20)
.build()
);
TextView tv = new TextView(getContext());
tv.setText(String.valueOf(files[i]));
tv.setTextSize(20);
tr.addView(tv);
final int fi = i;
tr.setOnClickListener(view -> {
@@ -4,14 +4,14 @@ import static com.ridgebotics.ridgescout.utility.FileEditor.baseDir;
import android.util.Log;
import com.ridgebotics.ridgescout.types.ColabArray;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder;
import com.ridgebotics.ridgescout.utility.FileEditor;
import com.ridgebotics.ridgescout.utility.HttpGetFile;
import com.ridgebotics.ridgescout.utility.HttpPutFile;
import com.ridgebotics.ridgescout.utility.RequestTask;
import com.ridgebotics.ridgescout.utility.SettingsManager;
import com.ridgebotics.ridgescout.utility.ToDelete;
import org.json.JSONException;
import org.json.JSONObject;
@@ -23,12 +23,15 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
// Class to synchronise data over HTTP.
// This is now deprsicated
// Class to syncronise data over FTP.
public class HttpSync extends Thread {
public static final String timestampsFilename = "timestamps";
@@ -98,10 +101,6 @@ public class HttpSync extends Thread {
public void run() {
isRunning = true;
boolean sendMetaFiles = SettingsManager.getFTPSendMetaFiles();
ToDelete.reload_todelete_list();
List<String> removeFiles = ToDelete.todelete_list.get();
String serverIP = SettingsManager.getFTPServer();
String serverKey = SettingsManager.getFTPKey();
@@ -119,9 +118,6 @@ public class HttpSync extends Thread {
getLocalFileMetadata();
localFiles.removeIf(localFile -> removeFiles.contains(localFile.filename+","+localFile.checksum));
remoteFiles.removeIf(remoteFile -> removeFiles.contains(remoteFile.filename+","+remoteFile.checksum));
@@ -134,30 +130,23 @@ public class HttpSync extends Thread {
TransferFile remoteFile = findInFileArray(remoteFiles, localFile.filename);
// Check if the file is a meta file, and uploads it based off of the setting
boolean sendField = (sendMetaFiles || !(localFile.filename.endsWith(".fields")));
boolean shouldUpload;
boolean special;
// If there is no file on the sever, upload.
if(remoteFile == null) {
shouldUpload = true;
special = false;
}
else {
// If the remote file is the same as the local one, do nothing.
boolean checksumsEqual = Objects.equals(localFile.checksum, remoteFile.checksum);
// If the local file is a colabarray, give it a special propreties
special = FileEditor.requiresSpecialInteraction(remoteFile.filename);
// If the local file is updated after the remote file
boolean after = after(localFile.updated, remoteFile.updated);
if(
(
sendMetaFiles || !(
localFile.filename.endsWith(".fields")
)
shouldUpload = !checksumsEqual && (special || after);
}
if(sendField && shouldUpload) {
uploadFile(localFile, serverIP, serverKey, special);
)
&& (remoteFile == null ||
(
!Objects.equals(localFile.checksum, remoteFile.checksum) &&
after(localFile.updated, remoteFile.updated)
)
)) {
uploadFile(localFile, serverIP, serverKey);
// await();
Log.d(getClass().toString(), "LocalFile: " + localFile.filename + ", " + localFile.checksum + ", " + localFile.updated + ": Uploaded");
upCount++;
}else {
@@ -174,24 +163,13 @@ public class HttpSync extends Thread {
TransferFile localFile = findInFileArray(localFiles, remoteFile.filename);
boolean shouldDownload;
// If there is no file on the sever, upload.
if(localFile == null) {
shouldDownload = true;
} else {
// If the remote file is the same as the local one, do nothing.
boolean checksumsNotEqual = !Objects.equals(localFile.checksum, remoteFile.checksum);
// If the local file is updated after the remote file
boolean after = after(remoteFile.updated, localFile.updated);
// If the local file and remote file's upload dates are exactly the same
boolean datesNotEqual = !localFile.updated.equals(remoteFile.updated);
shouldDownload = checksumsNotEqual && after;
}
if(shouldDownload) {
if(localFile == null ||
(
!Objects.equals(localFile.checksum, remoteFile.checksum) &&
after(remoteFile.updated, localFile.updated) &&
!localFile.updated.equals(remoteFile.updated)
)
) {
downloadFile(remoteFile, serverIP);
// await();
Log.d(getClass().toString(), "RemoteFile: " + remoteFile.filename + ", " + remoteFile.checksum + ", " + remoteFile.updated + ": Downloaded");
@@ -204,8 +182,8 @@ public class HttpSync extends Thread {
setUpdateIndicator("Downloading " + (Math.floor((double) (i * 1000) / remoteFiles.size()) / 10) + "%");
}
// Remove files marked for deletion
ToDelete.deleteFiles();
setUpdateIndicator("Finished, " + upCount + " Up, " + downCount + " Down");
@@ -215,7 +193,6 @@ public class HttpSync extends Thread {
}
// Find file based off of filename
private TransferFile findInFileArray(List<TransferFile> files, String filename){
for(TransferFile file : files) {
if(file.filename.equals(filename))
@@ -224,12 +201,29 @@ public class HttpSync extends Thread {
return null;
}
// Get teh last modified date of a file
private Date getLocalFileUtcTimestamp(File file) {
return new Date(file.lastModified());
}
// Load the local metadata of files
public static String getSHA256Hash(String filePath) throws IOException, NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
FileInputStream fis = new FileInputStream(filePath);
byte[] byteArray = new byte[1024];
int bytesCount = 0;
while ((bytesCount = fis.read(byteArray)) != -1) {
digest.update(byteArray, 0, bytesCount);
}
fis.close();
byte[] bytes = digest.digest();
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
private void getLocalFileMetadata() {
File localDir = new File(baseDir);
File[] localFileNames = localDir.listFiles();
@@ -246,10 +240,9 @@ public class HttpSync extends Thread {
tf.filename = file.getName();
tf.updated = getLocalFileUtcTimestamp(file);
try {
tf.checksum = FileEditor.getSHA256Hash(file.getName());
tf.checksum = getSHA256Hash(file.getPath());
} catch (Exception e) {
AlertManager.error("Failed to get hash of: " + file.getName(), e);
continue;
}
localFiles.add(tf);
}
@@ -285,100 +278,90 @@ public class HttpSync extends Thread {
await();
}
// Create HTTP request to upload file
void uploadFile(TransferFile tf, String serverURL, String apiKey, boolean special) {
// private boolean setTimestamps(Map<String, Date> timestamps){
// try {
// ByteBuilder bb = new ByteBuilder();
// String[] filenames = timestamps.keySet().toArray(new String[0]);
//
// for(int i = 0; i < filenames.length; i++){
// bb.addString(filenames[i]);
// bb.addLong(timestamps.get(filenames[i]).getTime());
// }
//
// FileEditor.writeFile(timestampsFilename, bb.build());
//
// uploadFile(new File(baseDir + timestampsFilename));
// return true;
// } catch (ByteBuilder.buildingException | IOException e) {
// AlertManager.error("Failed Syncing!", e);
// return false;
// }
// }
//
// private Map<String, Date> getTimestamps() {
// try {
// downloadFile(timestampsFilename, new File(baseDir + timestampsFilename));
//
// byte[] data = FileEditor.readFile(timestampsFilename);
//
// if(data == null || data.length == 0)
// return new HashMap<>();
//
// BuiltByteParser bbp = new BuiltByteParser(data);
// List<BuiltByteParser.parsedObject> pa = bbp.parse();
//
// Map<String, Date> output = new HashMap<>();
// for(int i = 0; i < pa.size(); i+=2){
//// System.out.println((long) pa.get(i).get());
// output.put(
// (String) pa.get(i).get(),
// new Date((long) pa.get(i+1).get())
// );
// }
// return output;
//
// }catch (IOException | BuiltByteParser.byteParsingExeption e){
// AlertManager.error("Failed Syncing!", e);
// return new HashMap<>();
// }
// }
void uploadFile(TransferFile tf, String serverURL, String apiKey) {
runningRequest.set(false);
// If the file is "special", download the server copy and merge the local and remote ColabArrays
if(special) {
HttpGetFile getTask = new HttpGetFile(serverURL + "/api/" + tf.filename, new File(baseDir + tf.filename), (stream, error) -> {
if(error != null) {
AlertManager.error(error);
return;
} else if (stream == null) {
AlertManager.error("Output stream from download was null!");
return;
}
byte[] bytes = stream.toByteArray();
FileEditor.syncColabArray(
tf.filename,
FileEditor.readFile(tf.filename),
bytes
);
HttpPutFile uploadTask = new HttpPutFile(serverURL + "/api/" + tf.filename, new File(baseDir + tf.filename), error2 -> {
if (error2 != null)
AlertManager.error(error2);
runningRequest.set(true);
}, new String[]{
"api_key: " + apiKey,
("modified: " + tf.updated.getTime())
});
uploadTask.execute();
});
getTask.execute();
} else {
// Upload the file
HttpPutFile uploadTask = new HttpPutFile(serverURL + "/api/" + tf.filename, new File(baseDir + tf.filename), error -> {
if (error != null)
HttpPutFile uploadTask = new HttpPutFile(serverURL + "/api/" + tf.filename, new File(baseDir + tf.filename), new HttpPutFile.UploadCallback() {
@Override
public void onResult(String error) {
if(error != null)
AlertManager.error(error);
runningRequest.set(true);
}, new String[]{
"api_key: " + apiKey,
("modified: " + tf.updated.getTime())
}); // Pass auth token if needed
}
}, new String[]{
"api_key: " + apiKey,
("modified: " + tf.updated.getTime())
}); // Pass auth token if needed
uploadTask.execute();
await();
}
uploadTask.execute();
await();
}
private void setLocalFileTimestamp(File file, Date date) {
file.setLastModified(date.getTime());
}
// Download a file from the remote server
void downloadFile(TransferFile tf, String serverURL) {
runningRequest.set(false);
File f = new File(baseDir + tf.filename);
HttpGetFile uploadTask = new HttpGetFile(serverURL + "/api/" + tf.filename, f, (stream, error) -> {
if(error != null) {
AlertManager.error(error);
return;
} else if (stream == null) {
AlertManager.error("Output stream from download was null!");
return;
HttpGetFile uploadTask = new HttpGetFile(serverURL + "/api/" + tf.filename, f, new HttpGetFile.DownloadCallback() {
@Override
public void onResult(String error) {
if(error != null)
AlertManager.error(error);
else
setLocalFileTimestamp(f, tf.updated);
runningRequest.set(true);
}
byte[] bytes = stream.toByteArray();
if(FileEditor.requiresSpecialInteraction(tf.filename)) {
FileEditor.syncColabArray(
tf.filename,
FileEditor.readFile(tf.filename),
bytes
);
} else {
FileEditor.writeFile(tf.filename, bytes);
}
setLocalFileTimestamp(f, tf.updated);
runningRequest.set(true);
});
}); // Pass auth token if needed
uploadTask.execute();
await();
@@ -8,11 +8,7 @@ import static com.ridgebotics.ridgescout.utility.FileEditor.TBAAddress;
import static com.ridgebotics.ridgescout.utility.FileEditor.TBAHeader;
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.util.Base64;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -26,7 +22,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.google.android.material.button.MaterialButton;
import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.databinding.FragmentTransferTbaBinding;
import com.ridgebotics.ridgescout.types.frcEvent;
@@ -38,7 +33,6 @@ import com.ridgebotics.ridgescout.utility.JSONUtil;
import com.ridgebotics.ridgescout.utility.RequestTask;
import com.ridgebotics.ridgescout.utility.FileEditor;
import com.ridgebotics.ridgescout.utility.SettingsManager;
import com.ridgebotics.ridgescout.utility.builders.TextViewBuilder;
import org.json.JSONArray;
import org.json.JSONException;
@@ -46,7 +40,6 @@ import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.function.Function;
// Class to download data from a specific event and encode it.
public class TBAEventFragment extends Fragment {
@@ -56,6 +49,8 @@ public class TBAEventFragment extends Fragment {
private final int year = SettingsManager.getYearNum();
private ProgressDialog loadingDialog;
private static JSONObject eventData = null;
public static void setEventData(JSONObject j){
eventData = j;
@@ -77,18 +72,27 @@ public class TBAEventFragment extends Fragment {
Table = binding.matchTable;
AlertManager.startLoading("Loading Teams and Matches...");
Table.setStretchAllColumns(true);
// Table.removeAllViews();
startLoading("Loading Teams and Matches...");
Table.removeAllViews();
Table.setStretchAllColumns(true);
Table.bringToFront();
TableRow tr1 = new TableRow(getContext());
addTableText(tr1, "Downloading Teams...");
Table.addView(tr1);
final RequestTask rq = new RequestTask();
rq.onResult(teamsStr -> {
TableRow tr11 = new TableRow(getContext());
addTableText(tr11, "Downloading Matches...");
Table.addView(tr11);
final RequestTask rq1 = new RequestTask();
rq1.onResult(matchesStr -> {
matchTable(matchesStr, teamsStr, eventData);
AlertManager.stopLoading();
stopLoading();
return null;
});
rq1.execute((TBAAddress + "event/" + matchKey + "/matches"), TBAHeader);
@@ -100,13 +104,18 @@ public class TBAEventFragment extends Fragment {
}
private void addTableText(TableRow tr, String textStr){
tr.addView(new TextViewBuilder(getContext(), textStr)
.size(18)
// .align_center()
.build());
TextView text = new TextView(getContext());
text.setTextSize(18);
text.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); // Text align center
text.setText(textStr);
tr.addView(text);
}
public void matchTable(String matchesString, String teamsString, JSONObject eventData){
Table.removeAllViews();
Table.setStretchAllColumns(true);
Table.bringToFront();
try {
final JSONArray matchData = new JSONArray(matchesString);
// final JSONArray matchData = new JSONArray();
@@ -121,19 +130,30 @@ public class TBAEventFragment extends Fragment {
}
// Event code at top
Table.addView(new TextViewBuilder(getContext(), matchKey)
.align_center()
.size(18)
.build());
TextView tv = new TextView(getContext());
tv.setLayoutParams(new TableRow.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setText(matchKey);
tv.setTextSize(18);
Table.addView(tv);
// Event Name
Table.addView(new TextViewBuilder(getContext(), matchName)
.align_center()
.size(28)
.build());
tv = new TextView(getContext());
tv.setLayoutParams(new TableRow.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText(matchName);
tv.setTextSize(28);
Table.addView(tv);
// Save button
MaterialButton btn = new MaterialButton(getContext());
Button btn = new Button(getContext());
btn.setText("Save");
btn.setTextSize(18);
btn.setLayoutParams(new TableRow.LayoutParams(
@@ -145,42 +165,68 @@ public class TBAEventFragment extends Fragment {
// If there are no matches, add the error.
// If there are no teams, don't allow the user to save the event and set the button to be invisible
if(teamData.length() == 0){
Table.addView(new TextViewBuilder(getContext(), "This event has no teams released yet...")
.align_center()
.size(18)
.build());
tv = new TextView(getContext());
tv.setLayoutParams(new TableRow.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText("This event has no teams released yet...");
tv.setTextSize(18);
Table.addView(tv);
tv = new TextView(getContext());
tv.setLayoutParams(new TableRow.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText("This event has no teams released yet...");
tv.setTextSize(18);
Table.addView(tv);
btn.setVisibility(View.GONE);
return;
}else if(matchData.length() == 0){
Table.addView(new TextViewBuilder(getContext(), "This event has no matches released yet...")
.align_center()
.size(18)
.build());
tv = new TextView(getContext());
tv.setLayoutParams(new TableRow.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText("This event has no matches released yet...");
tv.setTextSize(18);
Table.addView(tv);
Table.addView(new TextViewBuilder(getContext(), "Try manually adding practice matches.")
.align_center()
.size(18)
.build());
tv = new TextView(getContext());
tv.setLayoutParams(new TableRow.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText("Try manually adding practice matches.");
tv.setTextSize(18);
Table.addView(tv);
}
Table.addView(
new TextViewBuilder(getContext(), "Teams")
.align_center()
.size(28)
.build()
);
tv = new TextView(getContext());
tv.setLayoutParams(new TableRow.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText("Teams");
tv.setTextSize(28);
Table.addView(tv);
// Sort the teams into numerical order
int[] teams = new int[teamData.length()];
for(int i = 0 ; i < teamData.length(); i++){
@@ -189,26 +235,28 @@ public class TBAEventFragment extends Fragment {
Arrays.sort(teams);
// Loop through each match
TableRow tr = null;
for(int i=0; i < teamData.length(); i++){
// frcTeam team = event.teams.get(i);
int num = teams[i];
// If this is every 7th row, add the new row.
if(i % 7 == 0){
if(i != 0)
Table.addView(tr);
tr = new TableRow(getContext());
}
TextView text = new TextView(getContext());
text.setTextSize(18);
text.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
tr.addView(
new TextViewBuilder(getContext(), String.valueOf(num))
.align_center()
.size(18)
.build()
);
text.setText(String.valueOf(num));
// if(fileEditor.fileExist(event.eventCode + "-" + num + ".pitscoutdata")){
// text.setBackgroundColor(0x3000FF00);
// }else{
// text.setBackgroundColor(0x30FF0000);
// }
tr.addView(text);
}
if(tr != null)
Table.addView(tr);
@@ -221,13 +269,19 @@ public class TBAEventFragment extends Fragment {
Table.addView(
new TextViewBuilder(getContext(), "Matches")
.align_center()
.size(28)
.build()
);
tv = new TextView(getContext());
tv.setLayoutParams(new TableRow.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText("Matches");
tv.setTextSize(28);
Table.addView(tv);
tr = new TableRow(getContext());
addTableText(tr, "#");
addTableText(tr, "Red-1");
@@ -281,24 +335,22 @@ public class TBAEventFragment extends Fragment {
int[] redKeys = new int[3];
for(int b=0;b<6;b++){
TextViewBuilder text = new TextViewBuilder(getContext())
.size(18)
.align_center();
TextView text = new TextView(getContext());
text.setTextSize(18);
text.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); // Text align center
tr.addView(text);
if(b < 3){
String str = redAlliance.getString(b).substring(3);
redKeys[b] = Integer.parseInt(str);
text.text(str);
text.tv.setBackgroundColor(tba_red);
text.setText(str);
text.setBackgroundColor(tba_red);
}else{
String str = blueAlliance.getString(b-3).substring(3);
blueKeys[b-3] = Integer.parseInt(str);
text.text(str);
text.tv.setBackgroundColor(tba_blue);
text.setText(str);
text.setBackgroundColor(tba_blue);
}
tr.addView(text.build());
}
Table.addView(tr);
@@ -313,14 +365,22 @@ public class TBAEventFragment extends Fragment {
toggle = !toggle;
}
// btn.setOnClickListener(v -> {
// if(saveData(matchesOBJ, teamData, eventData)){
// alert("Info", "Saved!");
// }else{
// alert("Error", "Error saving files.");
// }
// });
}catch (JSONException j){
AlertManager.error("Failed Downloading", j);
AlertManager.stopLoading();
stopLoading();
}
}
private boolean saveData(ArrayList<frcMatch> matchData, JSONArray teamData, JSONObject eventData){
AlertManager.startLoading("Downloading team data...");
startLoading("Saving data...");
Thread t = new Thread(() -> {
try {
@@ -345,44 +405,16 @@ public class TBAEventFragment extends Fragment {
teamObj.country = team.getString("country");
teamObj.startingYear = team.getInt("rookie_year");
ImageRequestTask imageRequestTask = new ImageRequestTask();
RequestTask rq = new RequestTask();
rq.onResult(s -> {
try {
JSONArray jsonArray = new JSONArray(s);
JSONObject jsonObject = jsonArray.getJSONObject(0);
String base64 = jsonObject.getJSONObject("details").getString("base64Image");
imageRequestTask.onResult(bitmap -> {
teamObj.bitmap = bitmap;
teamObj.teamColor = frcTeam.findPrimaryColor(bitmap);
teams.add(teamObj);
byte[] decodedData = Base64.decode(base64, Base64.DEFAULT);
Bitmap bitmap = BitmapFactory.decodeByteArray(decodedData, 0, decodedData.length);
// System.out.println(base64);
teamObj.bitmap = bitmap;
teamObj.teamColor = frcTeam.findPrimaryColor(bitmap);
Log.i("TBA", "Got icon for team " + teamObj.teamNumber);
} catch (Exception e){
Log.i("TBA", "Failed to icon for team " + teamObj.teamNumber);
} finally {
teams.add(teamObj);
}
return null;
});
rq.execute((TBAAddress + "team/frc" + teamObj.teamNumber + "/media/" + year), TBAHeader);
// ImageRequestTask imageRequestTask = new ImageRequestTask();
//
// imageRequestTask.onResult(bitmap -> {
// teamObj.bitmap = bitmap;
// teamObj.teamColor = frcTeam.findPrimaryColor(bitmap);
// teams.add(teamObj);
//
// return null;
// });
// imageRequestTask.execute("https://www.thebluealliance.com/avatar/" + year + "/frc" + teamObj.teamNumber + ".png");
imageRequestTask.execute("https://www.thebluealliance.com/avatar/" + year + "/frc" + teamObj.teamNumber + ".png");
}
while (teams.size() != teamData.length()) {
@@ -399,15 +431,31 @@ public class TBAEventFragment extends Fragment {
AlertManager.toast("Saved!");
getActivity().runOnUiThread(() -> findNavController(this).navigate(R.id.action_navigation_tba_event_to_navigation_transfer));
AlertManager.stopLoading();
stopLoading();
}catch(Exception j) {
AlertManager.error(j);
AlertManager.stopLoading();
stopLoading();
}
});
t.start();
return false;
}
private void startLoading(String title){
getActivity().runOnUiThread(() -> {
if(loadingDialog != null && loadingDialog.isShowing())
loadingDialog.dismiss();
loadingDialog = ProgressDialog.show(getActivity(), title, "Please wait...");
});
}
private void stopLoading(){
getActivity().runOnUiThread(() -> {
if (loadingDialog != null)
loadingDialog.cancel();
loadingDialog = null;
});
}
}
@@ -25,7 +25,6 @@ import com.ridgebotics.ridgescout.ui.views.TBAEventOption;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.RequestTask;
import com.ridgebotics.ridgescout.utility.SettingsManager;
import com.ridgebotics.ridgescout.utility.builders.TextViewBuilder;
import org.json.JSONArray;
import org.json.JSONException;
@@ -55,6 +54,12 @@ public class TBASelectorFragment extends Fragment {
Table = binding.matchTable;
Table.setStretchAllColumns(true);
TableRow tr = new TableRow(getContext());
addTableText(tr, "Loading Events...");
Table.addView(tr);
startLoading("Loading Events...");
final RequestTask rq = new RequestTask();
@@ -72,6 +77,14 @@ public class TBASelectorFragment extends Fragment {
return binding.getRoot();
}
private void addTableText(TableRow tr, String textStr){
TextView text = new TextView(getContext());
text.setTextSize(18);
text.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); // Text align center
text.setText(textStr);
tr.addView(text);
}
public static int getEventTypeWeight(String type){
switch(type){
case "Preseason": return -3;
@@ -90,7 +103,7 @@ public class TBASelectorFragment extends Fragment {
public void eventTable(String dataString){
Table.removeAllViews();
// Table.setStretchAllColumns(true);
Table.setStretchAllColumns(true);
Table.bringToFront();
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
@@ -165,12 +178,12 @@ public class TBASelectorFragment extends Fragment {
try {
Date startDate = format.parse(j.getString("start_date"));
Date endDate = format.parse(j.getString("end_date"));
if(currentTime.after(startDate) && currentTime.before(endDate)) {
row.setColor(tba_current);
} else if(currentTime.after(endDate)){
if(currentTime.after(endDate)){
row.setColor(tba_previous);
}else if(currentTime.before(startDate)){
row.setColor(tba_next);
}else if(currentTime.after(startDate) && currentTime.before(endDate)){
row.setColor(tba_current);
}
} catch (Exception e) {
AlertManager.error("Failed finding start and end dates!", e);
@@ -1,135 +0,0 @@
package com.ridgebotics.ridgescout.ui.transfer.codes;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.AsyncTask;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.FileEditor;
import com.ridgebotics.ridgescout.utility.TaskRunner;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.function.Function;
public class CodeGenTask implements Callable<List<Bitmap>> {
// private Function<List<Bitmap>, String> resultFunction = null;
//
// @Override
// protected List<Bitmap> doInBackground(String... strings) {
//
//
// return new ArrayList<>();
// }
//
//
// public void onResult(Function<List<Bitmap>, String> func) {
// this.resultFunction = func;
// }
//
//
// @Override
// protected void onPostExecute(List<Bitmap> result) {
// super.onPostExecute(result);
// if(resultFunction != null){
// resultFunction.apply(result);
// }
// }
private final String data;
private final int randID;
private final int qrSize;
private final int qrCount;
private final int imageSize;
public CodeGenTask(String data, int randID, int qrSize, int qrCount, int imageSize) {
this.data = data;
this.randID = randID;
this.qrSize = qrSize;
this.qrCount = qrCount;
this.imageSize = imageSize;
}
@Override
public List<Bitmap> call() {
List<Bitmap> qrBitmaps = new ArrayList<>();
for(int i=0;i<=((data.length()+1)/qrSize);i++){
final int start = i*qrSize;
int end = (i+1)*qrSize;
if(end >= data.length()){
end = data.length();
}
try {
Bitmap unscaledBitmap = generateQrCode(
FileEditor.byteToChar(FileEditor.internalDataVersion, FileEditor.lengthHeaderBytes) +
String.valueOf(FileEditor.byteToChar(randID, FileEditor.lengthHeaderBytes)) +
FileEditor.byteToChar(i, FileEditor.lengthHeaderBytes) +
FileEditor.byteToChar(qrCount - 1, FileEditor.lengthHeaderBytes) +
data.substring(start, end)
);
if(unscaledBitmap == null) {
AlertManager.error("Generated image was null!");
continue;
}
qrBitmaps.add(Bitmap.createScaledBitmap(unscaledBitmap, imageSize, imageSize, false));
// alert("title", ""+(qrCount-1));
}catch (WriterException e){
AlertManager.error(e);
}
}
return qrBitmaps;
}
private Bitmap generateQrCode(String contents) throws WriterException {
final int size = 512;
if (contents == null) {
return null;
}
Map<EncodeHintType, Object> hints = new EnumMap<>(EncodeHintType.class);
// The Charset must be UTF-8, Or data will not be transferred properly. IDK why.
hints.put(EncodeHintType.CHARACTER_SET, "ISO-8859-1");
// hints.put(EncodeHintType.);
hints.put(EncodeHintType.MARGIN, 0); /* default = 4 */
MultiFormatWriter writer = new MultiFormatWriter();
BitMatrix result;
try {
result = writer.encode(contents, BarcodeFormat.DATA_MATRIX, size, size, hints);
} catch (IllegalArgumentException e) {
// Unsupported format
AlertManager.error(e);
return null;
}
int width = result.getWidth();
int height = result.getHeight();
int[] pixels = new int[width * height];
for (int y = 0; y < height; y++) {
int offset = y * width;
for (int x = 0; x < width; x++) {
pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.WHITE;
}
}
Bitmap bitmap = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
return bitmap;
}
}
@@ -1,10 +1,9 @@
package com.ridgebotics.ridgescout.ui.transfer.codes;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Handler;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -19,11 +18,16 @@ import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.databinding.FragmentTransferCodeSenderBinding;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.FileEditor;
import com.ridgebotics.ridgescout.utility.TaskRunner;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.EnumMap;
import java.util.Map;
import java.util.Random;
// Class to show the code transfer thing.
@@ -34,28 +38,23 @@ public class CodeGeneratorView extends Fragment {
private TextView qrIndexN;
private TextView qrIndexD;
private static final int maxQrCount = 256; //The max number that can be stored in a byte
private final int maxQrCount = 256; //The max number that can be stored in a byte
private static final int maxQrSize = 800;
private static final int maxQrSpeed = 50;
private static final int minQrSpeed = 1000;
private static final int defaultQrDelay = 12;
private int imageSize;
private final int maxQrSpeed = 5;
private final int minQrSpeed = 300 + maxQrSpeed - 1;
private int minQrSize = 0;
private final int maxQrSize = 800;
private int qrSize = 200;
private double qrDelay = 0;
private final int defaultQrDelay = 419;
private int qrDelay = 0;
private int qrIndex = 0;
private CountDownTimer timer;
private int qrCount = 0;
private List<Bitmap> qrBitmaps = new ArrayList<>();
private ArrayList<Bitmap> qrBitmaps;
private FragmentTransferCodeSenderBinding binding;
@@ -78,12 +77,6 @@ public class CodeGeneratorView extends Fragment {
qrIndexN = binding.qrIndexN;
qrIndexD = binding.qrIndexD;
DisplayMetrics displaymetrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
// int height = displaymetrics.heightPixels;
imageSize = displaymetrics.widthPixels;
// = 800;
String compressed = new String(FileEditor.blockCompress(data, FileEditor.lengthHeaderBytes), StandardCharsets.ISO_8859_1);
if(compressed.isEmpty()){
@@ -92,30 +85,75 @@ public class CodeGeneratorView extends Fragment {
}
minQrSize = Math.round((float)compressed.length() / maxQrCount)+1;
qrSize += minQrSize;
sendData(compressed);
qrSpeedSlider.setMax(maxQrSpeed*2);
qrSpeedSlider.setProgress(maxQrSpeed + defaultQrDelay);
qrSizeSlider.setMax(maxQrSize-minQrSize);
qrSizeSlider.setProgress(qrSize-minQrSize);
qrSpeedSlider.setMax((minQrSpeed-maxQrSpeed)*2);
startLoop();
qrSizeSlider.setProgress(minQrSize+qrSize);
qrSpeedSlider.setProgress(defaultQrDelay+5);
sendData(compressed);
return binding.getRoot();
}
private Bitmap generateQrCode(String contents) throws WriterException {
final int size = 512;
if (contents == null) {
return null;
}
Map<EncodeHintType, Object> hints = new EnumMap<>(EncodeHintType.class);
// The Charset must be UTF-8, Or data will not be transferred properly. IDK why.
hints.put(EncodeHintType.CHARACTER_SET, "ISO-8859-1");
// hints.put(EncodeHintType.);
hints.put(EncodeHintType.MARGIN, 0); /* default = 4 */
MultiFormatWriter writer = new MultiFormatWriter();
BitMatrix result;
try {
result = writer.encode(contents, BarcodeFormat.DATA_MATRIX, size, size, hints);
} catch (IllegalArgumentException e) {
// Unsupported format
AlertManager.error(e);
return null;
}
int width = result.getWidth();
int height = result.getHeight();
int[] pixels = new int[width * height];
for (int y = 0; y < height; y++) {
int offset = y * width;
for (int x = 0; x < width; x++) {
pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.WHITE;
}
}
Bitmap bitmap = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
return bitmap;
}
private void sendData(String data){
qrCount = (data.length()/qrSize)+1;
qrIndexD.setText(String.valueOf(qrCount));
// alert("size", ""+binding.qrSizeSlider.getProgress()+"\n"+binding.qrSizeSlider.getMax());
qrSpeedSlider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
qrDelay = ((double) progress /maxQrSpeed) - 1;
// startLoop();
qrDelay = -(minQrSpeed - progress - maxQrSpeed + 1);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {}
@@ -135,24 +173,43 @@ public class CodeGeneratorView extends Fragment {
qrCount = ((data.length()+1)/qrSize) +1;
qrIndexD.setText(String.valueOf(qrCount));
sendData(data);
// startLoop();
}
});
AlertManager.startLoading("Generating codes...");
// qrSizeSlider.setProgress(qr);
new TaskRunner().executeAsync(new CodeGenTask(data, new Random().nextInt(255), qrSize, qrCount, imageSize), result -> {
qrBitmaps = result;
AlertManager.stopLoading();
qrIndex = 0;
});
qrBitmaps = new ArrayList<>();
int randID = new Random().nextInt(255);
for(int i=0;i<=((data.length()+1)/qrSize);i++){
final int start = i*qrSize;
int end = (i+1)*qrSize;
if(end >= data.length()){
end = data.length();
}
try {
// alert("test", ""+Math.ceil((double)data.length()/(double)qrSize));
qrBitmaps.add(generateQrCode(
FileEditor.byteToChar(FileEditor.internalDataVersion, FileEditor.lengthHeaderBytes) +
String.valueOf(FileEditor.byteToChar(randID, FileEditor.lengthHeaderBytes)) +
FileEditor.byteToChar(i, FileEditor.lengthHeaderBytes) +
FileEditor.byteToChar(qrCount - 1, FileEditor.lengthHeaderBytes) +
data.substring(start, end)
));
// alert("title", ""+(qrCount-1));
}catch (WriterException e){
AlertManager.error(e);
}
}
qrIndex = 0;
if(timer != null){
timer.cancel();
}
qrLoop();
}
private void updateQr(){
if(qrBitmaps.isEmpty())
return;
qrImage.setImageBitmap(qrBitmaps.get(qrIndex));
if(qrDelay > 0) {
this.qrIndex += 1;
@@ -169,36 +226,13 @@ public class CodeGeneratorView extends Fragment {
qrIndexN.setText(String.valueOf(qrIndex+1));
}
private boolean shouldstop = false;
private void startLoop() {
final Handler handler = new Handler();
Runnable runnable = new Runnable() {
@Override
public void run() {
if(shouldstop){
return;
}
try{
updateQr();
}
catch (Exception e) {
AlertManager.error(e);
}
finally{
double a = ((double) maxQrSpeed) / (Math.abs(qrDelay));
a = Math.min(Math.max(a, maxQrSpeed), minQrSpeed);
handler.postDelayed(this, (long) a);
}
private void qrLoop(){
timer = new CountDownTimer(minQrSpeed-Math.abs(qrDelay)+1, 1000) {
public void onTick(long millisUntilFinished) {}
public void onFinish() {
updateQr();
qrLoop();
}
};
handler.post(runnable);
}
@Override
public void onDestroy() {
super.onDestroy();
shouldstop = true;
}.start();
}
}
@@ -65,7 +65,7 @@ public class CodeOverlayView extends View {
}
}
if(barColors != null){
final double width = (double) getWidth() /barColors.length;
final double width = getWidth()/barColors.length;
final int top = 0;
final int bottom = barHeight;
@@ -14,69 +14,19 @@ import com.google.zxing.Reader;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.datamatrix.DataMatrixReader;
import com.ridgebotics.ridgescout.utility.TaskRunner;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.function.Function;
// Background task for code scanning, to not slow down the scanner.
//public class CodeScanTask extends AsyncTask<String, String, String>{
// private Function<String, String> resultFunction = null;
// private Bitmap image;
//
// @Override
// protected String doInBackground(String... str) {
// if(image == null){return null;}
//
// int width = image.getWidth();
// int height = image.getHeight();
// int[] pixels = new int[width * height];
// image.getPixels(pixels, 0, width, 0, 0, width, height);
//
// RGBLuminanceSource source = new RGBLuminanceSource(width, height, pixels);
// BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));
//
// Map<DecodeHintType, Object> hints = new HashMap<>();
// hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
//// hints.put(DecodeHintType.PURE_BARCODE, Boolean.TRUE);
// hints.put(DecodeHintType.POSSIBLE_FORMATS, EnumSet.of(BarcodeFormat.DATA_MATRIX));
//
// Reader reader = new DataMatrixReader();
// try {
// Result result = reader.decode(binaryBitmap, hints);
// return result.getText();
// } catch (NotFoundException | ChecksumException | FormatException e) {
//// AlertManager.error(e);
// }
//
// return null;
// }
// public void setImage(Bitmap image){this.image = image;}
// public void onResult(Function<String, String> func) {
// this.resultFunction = func;
// }
//
// @Override
// protected void onPostExecute(String result) {
// super.onPostExecute(result);
// if(resultFunction != null){
// resultFunction.apply(result);
// }
// }
//}
public class CodeScanTask implements Callable<String> {
public class CodeScanTask extends AsyncTask<String, String, String>{
private Function<String, String> resultFunction = null;
private Bitmap image;
public CodeScanTask(Bitmap image) {
this.image = image;
}
@Override
public String call() {
protected String doInBackground(String... str) {
if(image == null){return null;}
int width = image.getWidth();
@@ -102,4 +52,17 @@ public class CodeScanTask implements Callable<String> {
return null;
}
}
public void setImage(Bitmap image){this.image = image;}
public void onResult(Function<String, String> func) {
this.resultFunction = func;
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
if(resultFunction != null){
resultFunction.apply(result);
}
}
}
@@ -6,7 +6,6 @@ import android.Manifest;
import android.app.AlertDialog;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.media.Image;
import android.os.Bundle;
import android.os.Handler;
@@ -39,15 +38,11 @@ import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.FileEditor;
import com.google.common.util.concurrent.ListenableFuture;
import com.ridgebotics.ridgescout.utility.TaskRunner;
import org.checkerframework.checker.units.qual.C;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -67,12 +62,21 @@ public class CodeScannerView extends Fragment {
alert.create().show();
}
private float scale = 0;
private final int downscale = 1;
private LifecycleOwner lifecycle;
private void setImage(Bitmap bmp){
if(scale == 0) {
scale = ((float) binding.container.getWidth() / bmp.getWidth()) * ((float) 16 / 9);
binding.scannerImage.setTranslationX(0);
binding.scannerImage.setTranslationY(0);
}
scanQRCode(bmp);
binding.scannerImage.setImageBitmap(bmp);
binding.scannerThreshold.bringToFront();
// alert("test", getChildCount()+"");
}
// private Bitmap img
@@ -95,38 +99,37 @@ public class CodeScannerView extends Fragment {
final int height = image.getHeight();
int[] pixels = new int[width * height];
for (int i = 0; i < width*height; i++) {
int L = levelMap[yBuffer.get(i) & 0xff];
pixels[i] = 0xff000000 | (L << 16) | (L << 8) | L;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int L = levelMap[yBuffer.get() & 0xff];
pixels[y * width + x] = 0xff000000 | (L << 16) | (L << 8) | L;
}
}
Matrix matrix = new Matrix();
matrix.postRotate(90);
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
// Bitmap.rota
return bitmap;
}
public void scanQRCode(Bitmap bitmap) {
// CodeScanTask async = new CodeScanTask();
new TaskRunner().executeAsync(new CodeScanTask(bitmap), data -> {
CodeScanTask async = new CodeScanTask();
async.setImage(bitmap);
async.onResult(data -> {
if(data != null){
// alert("test", ""+fileEditor.byteFromChar(data.charAt(0)));
compileData(
FileEditor.byteFromChar(data.charAt(0)),
FileEditor.byteFromChar(data.charAt(1)),
FileEditor.byteFromChar(data.charAt(2)),
(FileEditor.byteFromChar(data.charAt(3))+1),
data.substring(4)
FileEditor.byteFromChar(data.charAt(0)),
FileEditor.byteFromChar(data.charAt(1)),
FileEditor.byteFromChar(data.charAt(2)),
(FileEditor.byteFromChar(data.charAt(3))+1),
data.substring(4)
);
}
return null;
});
async.execute();
// return contents;
@@ -228,23 +231,26 @@ public class CodeScannerView extends Fragment {
void bindPreview(@NonNull ProcessCameraProvider cameraProvider) {
Preview preview = new Preview.Builder().build();
Preview preview = new Preview.Builder()
.setTargetAspectRatio(AspectRatio.RATIO_16_9)
.setTargetRotation(Surface.ROTATION_180)
.build();
CameraSelector cameraSelector = new CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
// .addCameraFilter(CameraFilters.NON)
.build();
ExecutorService executor = Executors.newSingleThreadExecutor();
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder()
// .setTargetAspectRatio(AspectRatio.RATIO_16_9)
// .setTargetResolution(new Size(224, 224))
.setOutputImageRotationEnabled(false)
.setTargetAspectRatio(AspectRatio.RATIO_16_9)
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
// .setOutputImageRotationEnabled(true)
// .setTargetRotation(Surface.ROTATION_0)
.build();
imageAnalysis.setAnalyzer(executor, new ImageAnalysis.Analyzer() {
@OptIn(markerClass = ExperimentalGetImage.class) @Override
public void analyze(@NonNull ImageProxy image) {
@@ -263,7 +269,6 @@ public class CodeScannerView extends Fragment {
});
cameraProvider.unbindAll();
// cameraProvider.ro
cameraProvider.bindToLifecycle(lifecycle,
cameraSelector, imageAnalysis, preview);
@@ -290,7 +295,6 @@ public class CodeScannerView extends Fragment {
Log.i("title", ""+qrCount);
barColors = new int[qrCount];
prevQrIndex = qrIndex;
qrScannedCount = 0;
}
final boolean updated;
@@ -309,20 +313,8 @@ public class CodeScannerView extends Fragment {
if(updated && qrScannedCount >= qrCount){
AlertManager.startLoading("Decoding data...");
new TaskRunner().executeAsync(new CodeDecodeTask(), result -> {
AlertManager.stopLoading();
});
}
prevQrIndex = qrIndex;
}
private class CodeDecodeTask implements Callable<Void> {
@Override
public Void call() {
String compiledString = "";
for(int i=0;i<qrDataArr.length;i++){
for(int i=0;i<qrCount;i++){
compiledString += qrDataArr[i];
}
@@ -350,8 +342,9 @@ public class CodeScannerView extends Fragment {
}catch (Exception e){
AlertManager.error(e);
}
return null;
}
prevQrIndex = qrIndex;
}
}
@@ -62,7 +62,7 @@ public class CustomSpinnerView extends LinearLayout {
this.index = defaultOption;
if(defaultOption != -1)
this.item.setText("" + options.get(defaultOption));
this.item.setText(options.get(defaultOption));
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
@@ -78,7 +78,7 @@ public class CustomSpinnerView extends LinearLayout {
CustomSpinnerPopup popup = new CustomSpinnerPopup(getContext()).init(options, option -> {
// dialog.();
if(!isEnabled()) return;
item.setText("" + option);
item.setText(option);
index = options.indexOf(option);
if(onClickListener != null) {
onClickListener.onClick(option, options.indexOf(option));
@@ -105,12 +105,12 @@ public class CustomSpinnerView extends LinearLayout {
}
public void setOption(String option) {
item.setText("" + option);
item.setText(option);
index = options.indexOf(option);
}
public void setOption(int index) {
item.setText("" + options.get(index));
item.setText(options.get(index));
this.index = index;
}
@@ -81,12 +81,6 @@ public class FieldPosView extends FrameLayout {
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;
}
}
@@ -1,7 +1,6 @@
package com.ridgebotics.ridgescout.ui.views;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.PorterDuff;
@@ -55,20 +54,6 @@ public class MatchScoutingIndicator extends RelativeLayout {
match_indicator_bar_team_num = findViewById(R.id.match_indicator_bar_team_num);
box = findViewById(R.id.file_indicator_box);
coloredBackground = findViewById(R.id.match_indicator_background);
int currentNightMode = getContext().getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
switch (currentNightMode) {
case Configuration.UI_MODE_NIGHT_NO:
// Night mode is not active on device
match_indicator_back_button.setColorFilter(Color.BLACK, PorterDuff.Mode.SRC_IN);
match_indicator_next_button.setColorFilter(Color.BLACK, PorterDuff.Mode.SRC_IN);
break;
case Configuration.UI_MODE_NIGHT_YES:
// Night mode is active on device
match_indicator_back_button.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN);
match_indicator_next_button.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN);
break;
}
}
public void setUsername(String username){
@@ -88,31 +73,19 @@ public class MatchScoutingIndicator extends RelativeLayout {
}
public void setColor(int color){
// Set color of main background rectangle
Drawable box_drawable = box.getBackground();
box_drawable.mutate();
box_drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
Drawable drawable = box.getBackground();
drawable.mutate();
drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
float[] hsv = new float[3];
Color.colorToHSV(color,hsv);
int background_color = Color.HSVToColor(220, new float[]{
hsv[0],
Math.min(hsv[1], 0.75f),
Math.min(hsv[2], 0.5f)
});
// Set color of main background rectangle, slightly dimmer
coloredBackground.setBackgroundColor(
background_color
Color.HSVToColor(220, new float[]{
hsv[0],
Math.min(hsv[1], 0.75f),
Math.min(hsv[2], 0.5f)
})
);
Drawable left_drawable = match_indicator_back_button.getBackground();
left_drawable.mutate();
left_drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
Drawable right_drawable = match_indicator_next_button.getBackground();
right_drawable.mutate();
right_drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
}
}
@@ -1,75 +0,0 @@
package com.ridgebotics.ridgescout.ui.views;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageButton;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.ridgebotics.ridgescout.R;
// A view for displaying information about a team.
public class PitScoutingIndicator extends RelativeLayout {
public PitScoutingIndicator(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public PitScoutingIndicator(Context context) {
super(context);
init(context);
}
public TextView pit_indicator_username;
public TextView pit_indicator_team_num;
private ConstraintLayout box;
private View coloredBackground;
public void init(Context context) {
LayoutInflater.from(context).inflate(R.layout.view_pit_scouting_indicator, this, true);
pit_indicator_username = findViewById(R.id.pit_indicator_username);
pit_indicator_team_num = findViewById(R.id.pit_indicator_teamnum);
box = findViewById(R.id.pit_indicator_box);
coloredBackground = findViewById(R.id.pit_indicator_background);
}
public void setUsername(String username){
pit_indicator_username.setText(username);
}
public void setTeamNum(int teamNum) {
pit_indicator_team_num.setText(String.valueOf(teamNum));
}
public void setColor(int color){
// Set color of main background rectangle
Drawable box_drawable = box.getBackground();
box_drawable.mutate();
box_drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
float[] hsv = new float[3];
Color.colorToHSV(color,hsv);
int background_color = Color.HSVToColor(220, new float[]{
hsv[0],
Math.min(hsv[1], 0.75f),
Math.min(hsv[2], 0.5f)
});
// Set color of main background rectangle, slightly dimmer
coloredBackground.setBackgroundColor(
background_color
);
}
}
@@ -3,7 +3,6 @@ package com.ridgebotics.ridgescout.utility;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.widget.Toast;
@@ -114,22 +113,4 @@ public class AlertManager {
});
}
private static ProgressDialog loadingDialog;
public static void startLoading(String title){
((Activity) context).runOnUiThread(() -> {
if(loadingDialog != null && loadingDialog.isShowing())
loadingDialog.dismiss();
loadingDialog = ProgressDialog.show(context, title, "Please wait...");
});
}
public static void stopLoading(){
((Activity) context).runOnUiThread(() -> {
if (loadingDialog != null)
loadingDialog.cancel();
loadingDialog = null;
});
}
}
@@ -6,7 +6,7 @@ import android.graphics.Color;
public class Colors {
// Lists and stuff
public static final int color_found = 0x7f00ff00;
public static final int color_rescout = 0xff007fff;
public static final int color_rescout = 0x7f0000ff;
public static final int color_not_found = 0x7f7f0000;
@@ -14,9 +14,9 @@ public class Colors {
public static final int unfocused_background_color = 0x50118811;
public static final int unsaved_color = 0xffaa0000;
public static final int saved_color = 0xff00aa00;
public static final int rescout_color = 0xff007fff;
public static final int unsaved_color = 0x60ff0000;
public static final int saved_color = 0x6000ff00;
public static final int rescout_color = 0x600000ff;
// Data graphs
@@ -67,9 +67,9 @@ public class Colors {
public static final int fileselector_unselected_color = 0x50006600;
// TBA
public static final int tba_previous = Color.argb(30, 64,64,64);
public static final int tba_current = 0x7f00ff00;
public static final int tba_next = Color.argb(30, 192,192,192);
public static final int tba_previous = 0x30FF0000;
public static final int tba_current = 0x50ff0000;
public static final int tba_next = 0x30FFFF00;
public static final int tba_red = 0x50ff0000;
public static final int tba_blue = 0x500000ff;
public static final int tba_toggle_background = 0x30000000;
@@ -2,10 +2,13 @@ package com.ridgebotics.ridgescout.utility;
import com.ridgebotics.ridgescout.scoutingData.Fields;
import com.ridgebotics.ridgescout.scoutingData.transfer.TransferType;
import com.ridgebotics.ridgescout.types.ColabArray;
import com.ridgebotics.ridgescout.types.frcEvent;
import com.ridgebotics.ridgescout.types.input.FieldType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
// Static class to hold loaded data, for ease of access.
public class DataManager {
public static String evcode;
@@ -59,39 +62,37 @@ public class DataManager {
}
}
public static ColabArray rescout_list = new ColabArray();
public static List<String> rescout_list = new ArrayList<>();
public static void reload_rescout_list(){
String filename = evcode + ".rescout";
if(!FileEditor.fileExist(filename)) {rescout_list = new ColabArray(); return;}
byte[] file = FileEditor.readFile(filename);
if(file == null) {rescout_list = new ColabArray(); return;}
if(!FileEditor.fileExist(evcode + ".rescout")) {rescout_list = new ArrayList<>(); return;}
byte[] file = FileEditor.readFile(evcode + ".rescout");
if(file == null) {rescout_list = new ArrayList<>(); return;}
try {
rescout_list = ColabArray.decode(file);
BuiltByteParser bbp = new BuiltByteParser(file);
rescout_list = new ArrayList<>(Arrays.asList((String[]) (bbp.parse().get(0).get())));
} catch (Exception e){
AlertManager.error("Error loading rescouting list", e);
rescout_list = new ColabArray();
AlertManager.error("Error loading scout fields", e);
rescout_list = new ArrayList<>();
}
}
public static void save_rescout_list() {
String filename = evcode + ".rescout";
try {
FileEditor.writeFile(filename, rescout_list.encode());
if(rescout_list.size() == 0){
FileEditor.deleteFile(evcode + ".rescout");
return;
}
ByteBuilder bb = new ByteBuilder();
bb.addStringArray(rescout_list.toArray(new String[0]));
FileEditor.writeFile(evcode + ".rescout", bb.build());
} catch (Exception e){
AlertManager.error("Error saving rescouting list", e);
AlertManager.error("Error saving scout fields", e);
}
}
public static String scoutNotice = "";
public static void reload_scout_notice(){
@@ -105,7 +106,7 @@ public class DataManager {
} catch (Exception e){
AlertManager.error("Error loading scout notice", e);
scoutNotice = "";
rescout_list = new ArrayList<>();
}
}
@@ -1,15 +1,7 @@
package com.ridgebotics.ridgescout.utility;
import static com.ridgebotics.ridgescout.utility.DataManager.match_transferValues;
import static com.ridgebotics.ridgescout.utility.DataManager.match_values;
import static com.ridgebotics.ridgescout.utility.DataManager.pit_transferValues;
import static com.ridgebotics.ridgescout.utility.DataManager.pit_values;
import android.annotation.SuppressLint;
import android.content.Context;
import com.ridgebotics.ridgescout.scoutingData.ScoutingDataWriter;
import com.ridgebotics.ridgescout.types.ColabArray;
import com.ridgebotics.ridgescout.types.frcEvent;
import com.ridgebotics.ridgescout.types.frcTeam;
@@ -23,8 +15,6 @@ import java.io.IOException;
import java.nio.BufferOverflowException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -37,17 +27,18 @@ import java.util.zip.Inflater;
// Helper class for binary editing
public final class FileEditor {
@SuppressLint("SdCardPath")
public final static String baseDir = "/data/data/com.ridgebotics.ridgescout/";
public static final byte internalDataVersion = 0x01;
public static final int maxCompressedBlockSize = 4096;
public static final int lengthHeaderBytes = 3;
public static final String TBAAddress = "https://www.thebluealliance.com/api/v3/";
// Hardcoded API key go brrr
public static final String TBAAddress = "https://www.thebluealliance.com/api/v3/";
public static final String TBAHeader = "X-TBA-Auth-Key: tjEKSZojAU2pgbs2mBt06SKyOakVhLutj3NwuxLTxPKQPLih11aCIwRIVFXKzY4e";
// private TimeZone localTimeZone = TimeZone.getDefault();
public static String binaryVisualize(byte[] bytes){
String returnStr = "";
@@ -109,7 +100,7 @@ public final class FileEditor {
public static int byteFromChar(char c){
byte[] bytes = (String.valueOf(c)).getBytes(StandardCharsets.ISO_8859_1);
byte[] bytes = (String.valueOf(c)).getBytes(Charset.defaultCharset());
return Byte.toUnsignedInt(bytes[0]);
}
@@ -242,19 +233,16 @@ public final class FileEditor {
// }
public static boolean writeFile(String filepath, byte[] data) {
return writeFile(new File(baseDir + filepath), data);
}
public static boolean writeFile(File file, byte[] data) {
public static boolean writeFile(String filepath, byte[] data) {
try {
FileOutputStream output = new FileOutputStream(file.getPath());
FileOutputStream output = new FileOutputStream(baseDir + filepath);
output.write(data);
output.close();
// Date d = new Date();
file.setLastModified(new Date().getTime());
new File(baseDir + filepath).setLastModified(new Date().getTime());
return true;
}
catch (IOException e) {
@@ -263,11 +251,6 @@ public final class FileEditor {
}
}
// Sets the date modified to a long, long time ago
public static boolean toTheArchaicPeriod(String name) {
return new File(baseDir + name).setLastModified(0);
}
public static boolean createFile(String filepath){
if(fileExist(filepath)){
return true;
@@ -296,15 +279,10 @@ public final class FileEditor {
}
public static byte[] readFile(String path){
return readFileExact(new File(baseDir + path));
return readFileExact(baseDir + path);
}
public static byte[] readFile(File path){
return readFileExact(path);
}
public static byte[] readFileExact(File file){
public static byte[] readFileExact(String path){
File file = new File(path);
int size = (int) file.length();
byte[] bytes = new byte[size];
try {
@@ -312,6 +290,9 @@ public final class FileEditor {
buf.read(bytes, 0, bytes.length);
buf.close();
return bytes;
} catch (FileNotFoundException e) {
AlertManager.error(e);
return null;
} catch (IOException e) {
AlertManager.error(e);
return null;
@@ -333,29 +314,6 @@ public final class FileEditor {
public static String getSHA256Hash(String filePath) throws IOException, NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
FileInputStream fis = new FileInputStream(baseDir + filePath);
byte[] byteArray = new byte[1024];
int bytesCount = 0;
while ((bytesCount = fis.read(byteArray)) != -1) {
digest.update(byteArray, 0, bytesCount);
}
fis.close();
byte[] bytes = digest.digest();
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
public static boolean setEvent(frcEvent event){
@@ -415,31 +373,34 @@ public final class FileEditor {
public static String[] getFiles(){
public static String[] getEventFiles(String evcode){
File f = new File(baseDir);
File[] files = f.listFiles();
if(files == null){return new String[0];}
List<String> outFiles = new ArrayList<>();
ArrayList<String> outFiles = new ArrayList<>();
outFiles.add("matches.fields");
outFiles.add("pits.fields");
// outFiles.add(evcode + ".eventdata");
for (File file : files) {
if (!file.isDirectory()) {
String name = file.getName();
if(!file.isDirectory() && name.startsWith(evcode)) {
outFiles.add(file.getName());
}
}
String[] filenames = outFiles.toArray(new String[0]);
try {
Arrays.sort(filenames, (o1, o2) -> {
try {
if (!o1.contains("-") || !o2.contains("-"))
return o2.compareTo(o1);
return Integer.valueOf(o1.split("-")[1].split("\\.")[0]).compareTo(Integer.valueOf(o2.split("-")[1].split("\\.")[0]));
return 0;
return Integer.valueOf(o1.split("-")[1]).compareTo(Integer.valueOf(o2.split("-")[1]));
} catch (Exception e) {
AlertManager.error(e);
return 0;
}
});
@@ -451,24 +412,6 @@ public final class FileEditor {
return filenames;
}
public static String[] getEventFiles(String evcode){
String[] files = getFiles();
List<String> outFiles = new ArrayList<>();
outFiles.add("matches.fields");
outFiles.add("pits.fields");
for (String file : files) {
if(file.startsWith(evcode)) {
outFiles.add(file);
}
}
return outFiles.toArray(new String[0]);
}
// https://stackoverflow.com/questions/7620401/how-to-convert-image-file-data-in-a-byte-array-to-a-bitmap
// public static String imageToBitMap(byte[] data) throws IOException {
// Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
@@ -481,72 +424,5 @@ public final class FileEditor {
public static boolean setTeams(Context context, String key, ArrayList<frcTeam> teams){
return true;
}
public static List<String> findCorruptedFiles() {
List<String> removeFiles = new ArrayList<>();
String[] localFiles = FileEditor.getFiles();
DataManager.reload_match_fields();
DataManager.reload_pit_fields();
for(int i = 0; i < localFiles.length; i++){
String filename = localFiles[i];
String[] split = filename.split("\\.");
String extention =split[split.length-1];
try {
switch (extention) {
case "matchscoutdata":
ScoutingDataWriter.load(filename, match_values, match_transferValues);
break;
case "pitscoutdata":
ScoutingDataWriter.load(filename, pit_values, pit_transferValues);
break;
default:
continue;
}
} catch (Exception e) {
removeFiles.add(filename);
}
}
return removeFiles;
}
public static boolean requiresSpecialInteraction(String name) {
// String name = file.getName();
if(!fileExist(name)) {
return false;
}
if(name.endsWith(".rescout")) {
return true;
}
return false;
}
public static void syncColabArray(String filename, byte[] currentBytes, byte[] newBytes) {
if(!fileExist(filename)) {
return;
}
try{
if(filename.endsWith(".rescout")) {
ColabArray colabArrayCurrent = ColabArray.decode(currentBytes);
ColabArray colabArrayNew = ColabArray.decode(newBytes);
colabArrayCurrent.append(colabArrayNew);
writeFile(filename, colabArrayCurrent.encode());
}
} catch (Exception e) {
AlertManager.error("Failed to sync ColabArray!", e);
}
}
}
@@ -5,16 +5,14 @@ import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
// Class to download remote file.
public class HttpGetFile extends AsyncTask<Void, Integer, File> {
public interface DownloadCallback {
void onResult(ByteArrayOutputStream bytes, String error);
void onResult(String error);
}
private String downloadUrl;
private File destinationFile;
private ByteArrayOutputStream outputStream;
private DownloadCallback callback;
private String errorMessage;
public HttpGetFile(String downloadUrl, File destinationFile, DownloadCallback callback) {
@@ -25,12 +23,9 @@ public class HttpGetFile extends AsyncTask<Void, Integer, File> {
@Override
protected File doInBackground(Void... voids) {
return run();
}
public File run() {
HttpURLConnection connection = null;
InputStream inputStream = null;
FileOutputStream outputStream = null;
try {
URL url = new URL(downloadUrl);
@@ -69,7 +64,7 @@ public class HttpGetFile extends AsyncTask<Void, Integer, File> {
}
}
outputStream = new ByteArrayOutputStream();
outputStream = new FileOutputStream(destinationFile);
byte[] buffer = new byte[8192];
long downloadedBytes = 0;
@@ -92,8 +87,6 @@ public class HttpGetFile extends AsyncTask<Void, Integer, File> {
}
outputStream.flush();
// FileEditor.writeFile(destinationFile, outputStream.toByteArray());
// Log.d(TAG, "Download successful. File saved to: " + destinationFile.getAbsolutePath());
return destinationFile;
@@ -111,7 +104,7 @@ public class HttpGetFile extends AsyncTask<Void, Integer, File> {
@Override
protected void onPostExecute(File result) {
if (callback != null) {
callback.onResult(outputStream, errorMessage);
callback.onResult(errorMessage);
}
}
@@ -119,7 +112,7 @@ public class HttpGetFile extends AsyncTask<Void, Integer, File> {
protected void onCancelled() {
deletePartialFile();
if (callback != null) {
callback.onResult(null, "Download cancelled");
callback.onResult("Download cancelled");
}
}
@@ -137,7 +130,8 @@ public class HttpGetFile extends AsyncTask<Void, Integer, File> {
return response.toString();
}
} catch (IOException e) {
AlertManager.error("Error reading error response", e);
AlertManager.error(e);
// Log.e(TAG, "Error reading error response", e);
}
return null;
}
@@ -145,9 +139,9 @@ public class HttpGetFile extends AsyncTask<Void, Integer, File> {
private void deletePartialFile() {
if (destinationFile != null && destinationFile.exists()) {
if (destinationFile.delete()) {
AlertManager.error("Partial download file deleted");
// Log.d(TAG, "Partial download file deleted");
} else {
AlertManager.error("Failed to delete partial download file");
// Log.w(TAG, "Failed to delete partial download file");
}
}
}
@@ -156,13 +150,15 @@ public class HttpGetFile extends AsyncTask<Void, Integer, File> {
try {
if (inputStream != null) inputStream.close();
} catch (IOException e) {
AlertManager.error("Error closing input stream", e);
AlertManager.error(e);
// Log.e(TAG, "Error closing input stream", e);
}
try {
if (outputStream != null) outputStream.close();
} catch (IOException e) {
AlertManager.error("Error closing output stream", e);
AlertManager.error(e);
// Log.e(TAG, "Error closing output stream", e);
}
if (connection != null) {
@@ -1,14 +1,11 @@
package com.ridgebotics.ridgescout.utility;
import android.annotation.SuppressLint;
import android.os.AsyncTask;
//import android.util.Log;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.atomic.AtomicReference;
// Class to send HTTP PUT request to upload file
public class HttpPutFile extends AsyncTask<Void, Integer, Boolean> {
// private static final String TAG = "FileUploadTask";
@@ -30,13 +27,8 @@ public class HttpPutFile extends AsyncTask<Void, Integer, Boolean> {
this.headers = headers;
}
@SuppressLint("WrongThread")
@Override
protected Boolean doInBackground(Void... voids) {
return run();
}
public boolean run() {
HttpURLConnection connection = null;
InputStream fileInputStream = null;
OutputStream outputStream = null;
@@ -57,8 +49,8 @@ public class HttpPutFile extends AsyncTask<Void, Integer, Boolean> {
connection.setUseCaches(false);
connection.setRequestProperty("Content-Type", "application/octet-stream");
connection.setRequestProperty("Content-Length", String.valueOf(fileToUpload.length()));
connection.setConnectTimeout(5000); // 5 seconds
connection.setReadTimeout(10000); // 10 seconds
connection.setConnectTimeout(30000); // 30 seconds
connection.setReadTimeout(60000); // 60 seconds
for(int i = 0; i < headers.length; i++){
String[] split = headers[i].split(": ");
@@ -103,8 +95,8 @@ public class HttpPutFile extends AsyncTask<Void, Integer, Boolean> {
}
} catch (Exception e) {
AlertManager.error(e);
errorMessage = "Upload error: " + e.getMessage();
AlertManager.error(errorMessage, e);
// Log.e(TAG, errorMessage, e);
return false;
} finally {
@@ -141,23 +133,25 @@ public class HttpPutFile extends AsyncTask<Void, Integer, Boolean> {
return response.toString();
}
} catch (IOException e) {
AlertManager.error("Error reading error response", e);
AlertManager.error(e);
// Log.e(TAG, "Error reading error response", e);
}
return null;
}
// Clean up stream
private void closeResources(InputStream inputStream, OutputStream outputStream, HttpURLConnection connection) {
try {
if (inputStream != null) inputStream.close();
} catch (IOException e) {
AlertManager.error("Error closing input stream", e);
AlertManager.error(e);
// Log.e(TAG, "Error closing input stream", e);
}
try {
if (outputStream != null) outputStream.close();
} catch (IOException e) {
AlertManager.error("Error closing output stream", e);
AlertManager.error(e);
// Log.e(TAG, "Error closing output stream", e);
}
if (connection != null) {
@@ -25,24 +25,11 @@ public class ImageRequestTask extends AsyncTask<String, Void, Bitmap> {
try {
URL url = new URL(src);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// We do a little bit of spoofing
connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11");
connection.setDoInput(true);
connection.connect();
int code = connection.getResponseCode();
switch (code) {
case 200:
InputStream input = connection.getInputStream();
return BitmapFactory.decodeStream(input);
case 403:
// AlertManager.error("Got 403, Going to https://www.thebluealliance.com/avatars may fix this");
return null;
default:
AlertManager.error("Error downloading image " + src, "Got response code: " + code);
return null;
}
} catch (FileNotFoundException e){
InputStream input = connection.getInputStream();
return BitmapFactory.decodeStream(input);
} catch (FileNotFoundException e) {
return null;
} catch (IOException e){
AlertManager.error("Error downloading image " + src, e);
@@ -46,8 +46,8 @@ public class SettingsManager {
hm.put(UnameKey, "Username");
hm.put(SelEVCodeKey, "unset");
hm.put(WifiModeKey, false);
hm.put(YearNumKey, 2026);
hm.put(FieldImageKey, "2026");
hm.put(YearNumKey, 2025);
hm.put(FieldImageKey, "2025");
hm.put(MatchNumKey, 0);
hm.put(AllyPosKey, "red-1");
hm.put(DataModeKey, 0);
@@ -1,32 +0,0 @@
package com.ridgebotics.ridgescout.utility;
import android.os.Handler;
import android.os.Looper;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
// https://stackoverflow.com/questions/58767733/the-asynctask-api-is-deprecated-in-android-11-what-are-the-alternatives
public class TaskRunner {
private final Executor executor = Executors.newSingleThreadExecutor(); // change according to your requirements
private final Handler handler = new Handler(Looper.getMainLooper());
public interface Callback<R> {
void onComplete(R result);
}
public <R> void executeAsync(Callable<R> callable, Callback<R> callback) {
executor.execute(() -> {
final R result;
try {
result = callable.call();
handler.post(() -> {
callback.onComplete(result);
});
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
}
@@ -1,150 +0,0 @@
package com.ridgebotics.ridgescout.utility;
import static android.widget.LinearLayout.VERTICAL;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.widget.CheckBox;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import com.ridgebotics.ridgescout.types.ColabArray;
import com.ridgebotics.ridgescout.utility.builders.TextViewBuilder;
import java.util.ArrayList;
import java.util.List;
public class ToDelete {
public static final String filename = "todelete.colabarray";
public static void findCorruptedFiles(Context c) {
new Thread(() -> {
AlertManager.startLoading("Loading files...");
List<String> filenames = FileEditor.findCorruptedFiles();
AlertManager.stopLoading();
((Activity) c).runOnUiThread(() -> {
deleteFiles(c, filenames, true);
});
}).start();
}
public static void deleteFiles(Context c, List<String> files, boolean defaultOption) {
ScrollView sv = new ScrollView(c);
LinearLayout ll = new LinearLayout(c);
ll.setOrientation(VERTICAL);
sv.addView(ll);
CheckBox[] checkboxes = new CheckBox[files.size()];
for(int i =0; i < files.size(); i++){
CheckBox cb = new CheckBox(c);
cb.setText(files.get(i));
cb.setChecked(defaultOption);
ll.addView(cb);
checkboxes[i] = cb;
}
AlertDialog.Builder alert = new AlertDialog.Builder(c);
alert.setTitle("Delete files");
alert.setView(sv);
alert.setNeutralButton("Cancel", null);
alert.setPositiveButton("Delete", (_dialogInterface, _i) -> {
List<String> delete_files = new ArrayList<>();
for(int i = 0; i < files.size(); i++) {
if(checkboxes[i].isChecked())
delete_files.add(files.get(i));
}
AlertDialog.Builder confirm = new AlertDialog.Builder(c);
alert.setTitle("Confirm");
alert.setView(new TextViewBuilder(c, "Are you sure you want to delete " + delete_files.size() + " files?").build());
alert.setNeutralButton("Cancel", null);
alert.setPositiveButton("Delete", (dialogInterface, i) -> {
deleteFiles(delete_files);
});
alert.setCancelable(false);
alert.create().show();
});
alert.setCancelable(false);
alert.create().show();
}
public static ColabArray todelete_list = new ColabArray();
public static void reload_todelete_list(){
if(!FileEditor.fileExist(ToDelete.filename)) {todelete_list = new ColabArray(); return;}
byte[] file = FileEditor.readFile(ToDelete.filename);
if(file == null) {todelete_list = new ColabArray(); return;}
try {
todelete_list = ColabArray.decode(file);
} catch (Exception e){
AlertManager.error("Error loading todelete list", e);
todelete_list = new ColabArray();
}
}
public static void save_todelete_list() {
try {
FileEditor.writeFile(ToDelete.filename, todelete_list.encode());
} catch (Exception e){
AlertManager.error("Error saving todelete list", e);
}
}
private static void deleteFiles(List<String> toDelete) {
reload_todelete_list();
for(String file : toDelete) {
String hash;
try {
hash = FileEditor.getSHA256Hash(file);
} catch (Exception e) {
AlertManager.error("Failed to get hash of file: " + file, e);
continue;
}
todelete_list.add(file+","+hash);
FileEditor.deleteFile(file);
}
save_todelete_list();
}
public static void deleteFiles() {
reload_todelete_list();
List<String> toDelete = todelete_list.get();
for(String filename : FileEditor.getFiles()){
try {
String hash = FileEditor.getSHA256Hash(filename);
if(toDelete.contains(filename+","+hash)) {
FileEditor.deleteFile(filename);
}
} catch (Exception e) {
AlertManager.error("Failed to get hash of file: " + filename, e);
continue;
}
}
}
public static boolean contains(String localfile) {
try {
String hash = FileEditor.getSHA256Hash(localfile);
return contains(localfile, hash);
} catch (Exception e) {
AlertManager.error("Failed to get hash of file: " + localfile, e);
return false;
}
}
public static boolean contains(String filename, String hash){
return todelete_list.contains(filename+","+hash);
}
}
@@ -1,161 +0,0 @@
package com.ridgebotics.ridgescout.utility.builders;
import android.content.Context;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TableRow;
import android.widget.TextView;
public class TextViewBuilder {
public TextView tv;
public TextViewBuilder(Context c) {
tv = new TextView(c);
}
public TextViewBuilder(Context c, String str) {
tv = new TextView(c);
tv.setText(str);
}
public TextViewBuilder size(float size) {
tv.setTextSize(size);
return this;
}
public TextViewBuilder text(String str) {
tv.setText(str);
return this;
}
public TextViewBuilder padding(int borders) {
tv.setPadding(borders,borders,borders,borders);
return this;
}
public TextViewBuilder padding(int horisontal, int vertical) {
tv.setPadding(horisontal,vertical,horisontal,vertical);
return this;
}
public TextViewBuilder padding(int left, int right, int top, int bottom) {
tv.setPadding(left,top,right,bottom);
return this;
}
public TextViewBuilder align_left() {
tv.setGravity(Gravity.START);
tv.setTextAlignment(View.TEXT_ALIGNMENT_TEXT_START);
return this;
}
public TextViewBuilder align_center() {
tv.setGravity(Gravity.CENTER_HORIZONTAL);
// FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
// ViewGroup.LayoutParams.MATCH_PARENT,
// ViewGroup.LayoutParams.WRAP_CONTENT
// );
// params.gravity = Gravity.CENTER;
// tv.setLayoutParams(params);
//
//
tv.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
return this;
}
// public TextViewBuilder center_xy() {
// TableRow.LayoutParams params = new TableRow.LayoutParams();
// params.gravity = Gravity.CENTER;
// tv.setLayoutParams(params);
// return this;
// }
public TextViewBuilder align_right() {
tv.setGravity(Gravity.END);
tv.setTextAlignment(View.TEXT_ALIGNMENT_TEXT_END);
return this;
}
public TextViewBuilder layout_wrap_wrap() {
tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
return this;
}
public TextViewBuilder layout_wrap_match() {
tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.MATCH_PARENT
));
return this;
}
public TextViewBuilder layout_match_wrap() {
tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
return this;
}
public TextViewBuilder layout_match_match() {
tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
));
return this;
}
public TextViewBuilder h1() {
tv.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Headline1);
return this;
}
public TextViewBuilder h2() {
tv.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Headline2);
return this;
}
public TextViewBuilder h3() {
tv.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Headline3);
return this;
}
public TextViewBuilder h4() {
tv.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Headline4);
return this;
}
public TextViewBuilder h5() {
tv.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Headline5);
return this;
}
public TextViewBuilder h6() {
tv.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Headline6);
return this;
}
public TextViewBuilder sub1() {
tv.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Subtitle1);
return this;
}
public TextViewBuilder sub2() {
tv.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Subtitle2);
return this;
}
public TextViewBuilder body1() {
tv.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Body1);
return this;
}
public TextViewBuilder body2() {
tv.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Body2);
return this;
}
public TextView build() {
return tv;
}
}
BIN
View File
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 636 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 954 KiB

@@ -29,38 +29,14 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
tools:layout_editor_absoluteX="0dp" >
</com.ridgebotics.ridgescout.ui.views.TeamCard>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<Button
android:id="@+id/tbaButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="3dp"
android:layout_weight="1"
android:text="View on TBA" />
<Button
android:id="@+id/statboticsButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="3dp"
android:layout_weight="1"
android:text="View on Statbotics" />
</LinearLayout>
tools:layout_editor_absoluteX="0dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Pit Data"
android:textAlignment="center"
android:textSize="35sp"
android:textSize="24sp"
app:layout_constraintTop_toBottomOf="@+id/team_description2"
tools:layout_editor_absoluteX="0dp" />
@@ -73,11 +49,6 @@
app:layout_constraintStart_toStartOf="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>
<TextView
@@ -86,7 +57,7 @@
android:layout_height="wrap_content"
android:text="Match Data"
android:textAlignment="center"
android:textSize="35sp"
android:textSize="24sp"
app:layout_constraintTop_toBottomOf="@+id/team_description2"
tools:layout_editor_absoluteX="0dp" />
@@ -144,13 +115,9 @@
app:layout_constraintStart_toStartOf="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>
</ScrollView>
+11 -9
View File
@@ -92,6 +92,17 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/text_next_match"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:text="TextView"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_name" />
<TextView
android:id="@+id/text_match_alliance"
android:layout_width="wrap_content"
@@ -111,15 +122,6 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/event_button" />
<LinearLayout
android:id="@+id/info_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_name" />
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -45,7 +45,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Scouting Notice"
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"/>
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"></TextView>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -4,27 +4,54 @@
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.ridgebotics.ridgescout.ui.views.PitScoutingIndicator
android:id="@+id/pit_indicator"
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/pit_file_indicator"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_height="wrap_content"
android:background="#60ff0000"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/pit_bar_team_num"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="4388"
android:textAlignment="center"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/pitUsername"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Username"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/pit_bar_team_num"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<LinearLayout
android:id="@+id/pitScoutArea"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingTop="60dp">
android:paddingTop="48dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/scouting_notice_box"
@@ -33,15 +60,16 @@
android:layout_alignParentEnd="true"
android:layout_margin="5dp"
android:background="@drawable/border"
android:orientation="horizontal"
android:padding="10dp">
android:padding="10dp"
android:orientation="horizontal">
<TextView
android:id="@+id/scouting_notice_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Scouting Notice"
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"></TextView>
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
android:text="Scouting Notice">
</TextView>
</androidx.constraintlayout.widget.ConstraintLayout>
+42 -56
View File
@@ -10,6 +10,7 @@
android:id="@+id/scrollView2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="60dp"
android:fillViewport="true"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
@@ -17,70 +18,55 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
<TableLayout
android:id="@+id/SettingsTable"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TableLayout
android:id="@+id/SettingsTable"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
<!-- <Button-->
<!-- android:id="@+id/scoutNoticeButton"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_weight="1"-->
<!-- android:layout_margin="5dp"-->
<!-- android:text="Edit Scout notice"-->
<!-- android:textSize="16sp" />-->
<!-- <LinearLayout-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:orientation="horizontal">-->
<!-- <Button-->
<!-- android:id="@+id/fieldsPitsButton"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_weight="1"-->
<!-- android:layout_margin="5dp"-->
<!-- android:text="Edit PIT Fields"-->
<!-- android:textSize="16sp" />-->
<!-- <Button-->
<!-- android:id="@+id/fieldsMatchesButton"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_weight="1"-->
<!-- android:layout_margin="5dp"-->
<!-- android:text="Edit MATCH Fields"-->
<!-- android:textSize="16sp" />-->
<!-- </LinearLayout>-->
</LinearLayout>
</TableLayout>
</ScrollView>
<!-- <LinearLayout-->
<!-- android:id="@+id/fieldsButtons"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_marginTop="1dp"-->
<!-- android:gravity="center"-->
<!-- android:orientation="horizontal"-->
<!-- android:visibility="gone"-->
<!-- app:layout_constraintEnd_toEndOf="parent"-->
<!-- app:layout_constraintStart_toStartOf="parent"-->
<!-- app:layout_constraintBottom_toTopOf="@+id/fieldsButton"-->
<!-- tools:visibility="visible">-->
<Button
android:id="@+id/fieldsButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Fields"
android:textSize="34sp"
app:layout_constraintBottom_toBottomOf="@+id/scrollView2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<!-- </LinearLayout>-->
<LinearLayout
android:id="@+id/fieldsButtons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="1dp"
android:gravity="center"
android:orientation="horizontal"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="@+id/fieldsButton"
tools:visibility="visible">
<Button
android:id="@+id/fieldsPitsButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:text="Pits"
android:textSize="34sp" />
<Button
android:id="@+id/fieldsMatchesButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:text="Matches"
android:textSize="34sp" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -14,7 +14,8 @@
android:id="@+id/teams"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" />
android:orientation="vertical"
android:paddingTop="48dp" />
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -7,65 +7,77 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
<SeekBar
android:id="@+id/scannerColors"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginTop="15dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<SeekBar
android:id="@+id/scannerThreshold"
android:layout_width="match_parent"
android:layout_height="48dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/scannerColors" />
<SeekBar
android:id="@+id/scannerBrightness"
android:layout_width="match_parent"
android:layout_height="48dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/scannerThreshold" />
<TextView
android:id="@+id/scannerColorsLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginBottom="-12dp"
android:text="Posterize"
app:layout_constraintBottom_toTopOf="@+id/scannerColors"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/scannerThresholdLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginBottom="-12dp"
android:text="Exposure"
app:layout_constraintBottom_toTopOf="@+id/scannerThreshold"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/scannerBrightnessLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginBottom="-12dp"
android:text="Brightness"
app:layout_constraintBottom_toTopOf="@+id/scannerBrightness"
app:layout_constraintStart_toStartOf="parent" />
<ImageView
android:id="@+id/scannerImage"
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:adjustViewBounds="true"
android:rotation="90"
android:scaleType="fitCenter"
android:scaleX="1"
android:scaleY="1"
android:translationY="-40dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:context=".CodeScannerView" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="3dp"
android:padding="3dp"
android:background="@drawable/border"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
tools:layout_editor_absoluteX="3dp">
<TextView
android:id="@+id/scannerColorsLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Posterize"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"/>
<SeekBar
android:id="@+id/scannerColors"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginTop="15dp" />
<TextView
android:id="@+id/scannerThresholdLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Exposure"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"/>
<SeekBar
android:id="@+id/scannerThreshold"
android:layout_width="match_parent"
android:layout_height="48dp" />
<TextView
android:id="@+id/scannerBrightnessLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Brightness"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"/>
<SeekBar
android:id="@+id/scannerBrightness"
android:layout_width="match_parent"
android:layout_height="48dp" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,10 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<SeekBar
android:id="@+id/qrSizeSlider"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginBottom="60dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/qrImage" />
<SeekBar
android:id="@+id/qrSpeedSlider"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginBottom="60dp"
app:layout_constraintBottom_toTopOf="@+id/qrSizeSlider"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.971"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/qrImage"
app:layout_constraintVertical_bias="0.93" />
<ImageView
android:id="@+id/qrImage"
android:layout_width="match_parent"
@@ -15,48 +36,23 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:layout_width="match_parent"
<TextView
android:id="@+id/qrSpeedText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="3dp"
android:orientation="vertical"
android:background="@drawable/border"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
android:layout_marginStart="8dp"
android:text="QR Speed"
app:layout_constraintBottom_toTopOf="@+id/qrSpeedSlider"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/qrSpeedText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="QR Speed"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
app:layout_constraintBottom_toTopOf="@+id/qrSpeedSlider" />
<SeekBar
android:id="@+id/qrSpeedSlider"
android:layout_width="match_parent"
android:layout_height="48dp" />
<TextView
android:id="@+id/qrSizeText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="QR Size"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6" />
<SeekBar
android:id="@+id/qrSizeSlider"
android:layout_width="match_parent"
android:layout_height="48dp"
tools:layout_editor_absoluteY="135dp" />
</LinearLayout>
<TextView
android:id="@+id/qrSizeText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="QR Size"
app:layout_constraintBottom_toTopOf="@+id/qrSizeSlider"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/qrIndexN"
@@ -64,8 +60,7 @@
android:layout_height="wrap_content"
android:text="0"
app:layout_constraintEnd_toStartOf="@+id/qrIndexSlash"
app:layout_constraintTop_toBottomOf="@+id/qrImage"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"/>
app:layout_constraintTop_toBottomOf="@+id/qrImage" />
<TextView
android:id="@+id/qrIndexSlash"
@@ -74,8 +69,7 @@
android:text="/"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/qrImage"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6" />
app:layout_constraintTop_toBottomOf="@+id/qrImage" />
<TextView
android:id="@+id/qrIndexD"
@@ -83,7 +77,6 @@
android:layout_height="wrap_content"
android:text="0"
app:layout_constraintStart_toEndOf="@+id/qrIndexSlash"
app:layout_constraintTop_toBottomOf="@+id/qrImage"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6" />
app:layout_constraintTop_toBottomOf="@+id/qrImage" />
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -8,12 +8,9 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="3dp"
android:layout_marginTop="5dp"
android:layout_marginRight="3dp"
android:layout_marginBottom="3dp"
android:background="@drawable/border"
android:layout_margin="3dp"
android:orientation="horizontal"
android:background="@drawable/border"
tools:ignore="UselessParent">
<TextView
@@ -21,9 +18,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp"
android:overlapAnchor="false"
android:text="▼ Options"
android:textSize="24sp" />
android:textSize="24sp"
android:overlapAnchor="false"/>
</LinearLayout>
@@ -32,9 +28,9 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="9dp"
android:layout_marginTop="-6dp"
android:layout_marginTop="-5dp"
android:paddingLeft="3dp"
android:paddingRight="3dp"
android:text="Test"
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1" />
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"/>
</RelativeLayout>
@@ -11,6 +11,7 @@
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:background="@drawable/border"
android:padding="3dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
@@ -22,18 +23,16 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="gone" />
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
android:id="@+id/match_indicator_back_button"
android:layout_width="50dp"
android:layout_height="50dp"
android:contentDescription="Back"
android:scaleType="fitCenter"
android:src="@drawable/triangle_left"
android:text="Back"
android:background="@drawable/border"
android:background="@color/zxing_transparent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
@@ -43,10 +42,9 @@
android:layout_width="50dp"
android:layout_height="50dp"
android:scaleType="fitCenter"
android:contentDescription="Next"
android:src="@drawable/triangle_right"
android:text="Next"
android:background="@drawable/border"
android:background="@color/zxing_transparent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
@@ -55,24 +53,24 @@
android:id="@+id/match_indicator_alliance_pos_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="58dp"
android:layout_marginEnd="4dp"
android:text="Temp"
android:textAlignment="center"
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
android:textAlignment="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/match_indicator_next_button"
app:layout_constraintEnd_toStartOf="@+id/match_indicator_next_button"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/match_indicator_username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="58dp"
android:layout_marginStart="4dp"
android:text="Temp"
android:textAlignment="center"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintStart_toEndOf="@+id/match_indicator_back_button"
app:layout_constraintTop_toTopOf="parent" />
<TextView
@@ -1,55 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/pit_indicator_box"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:background="@drawable/border"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<View
android:id="@+id/pit_indicator_background"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="gone" />
<TextView
android:id="@+id/pit_indicator_username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="58dp"
android:text="Username"
android:textAlignment="center"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/pit_indicator_teamnum"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:text="4388"
android:textAlignment="center"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</RelativeLayout>
@@ -17,8 +17,6 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="0"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
android:textSize="24sp" />
<Button
+1 -1
View File
@@ -28,7 +28,7 @@
android:id="@+id/team_option_logo"
android:layout_width="54dp"
android:layout_height="54dp"
android:layout_margin="2dp"
android:layout_margin="3dp"
android:scaleType="fitXY"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1"
+1 -1
View File
@@ -1,5 +1,5 @@
[versions]
agp = "8.13.2"
agp = "8.8.0"
junit = "4.13.2"
junitVersion = "1.1.5"
espressoCore = "3.5.1"
+1 -1
View File
@@ -1,6 +1,6 @@
#Sun Mar 24 10:48:55 MDT 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
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: 493 KiB

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 434 KiB

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 454 KiB

After

Width:  |  Height:  |  Size: 162 KiB

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