Attempt to get F-Droid working

This commit is contained in:
Astatin3
2024-09-15 22:47:45 -06:00
parent 138158e2ee
commit 3f88c9a05e
74 changed files with 372 additions and 366 deletions
@@ -0,0 +1,155 @@
package com.ridgebotics.ridgescout;
import android.content.Intent;
import android.os.Bundle;
import android.view.MenuItem;
import com.ridgebotics.ridgescout.scoutingData.fields;
import com.ridgebotics.ridgescout.utility.SentimentAnalysis;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.fileEditor;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.navigation.NavController;
import androidx.navigation.NavOptions;
import androidx.navigation.Navigation;
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;
import com.ridgebotics.ridgescout.databinding.ActivityMainBinding;
import com.ridgebotics.ridgescout.SettingsVersionStack.latestSettings;
import com.google.android.material.navigation.NavigationBarView;
import java.util.Objects;
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
private BottomNavigationView navView;
private AppBarConfiguration appBarConfiguration;
private NavController navController;
@Override
protected void onCreate(Bundle savedInstanceState) {
latestSettings.update();
if(!fileEditor.fileExist(fields.matchFieldsFilename)){
fields.save(fields.matchFieldsFilename, fields.default_match_fields);
}
if(!fileEditor.fileExist(fields.pitsFieldsFilename)){
fields.save(fields.pitsFieldsFilename, fields.default_pit_fields);
}
AlertManager.init(this);
SentimentAnalysis.init(this);
Objects.requireNonNull(getSupportActionBar()).hide();
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
navView = findViewById(R.id.nav_view);
// appBarConfiguration = new AppBarConfiguration.Builder(
// R.id.navigation_scouting,
// R.id.navigation_match_scouting,
// R.id.navigation_team_selector,
// R.id.navigation_pit_scouting,
//
// R.id.navigation_data,
// R.id.navigation_data_status,
// R.id.navigation_data_teams,
// R.id.navigation_data_compile,
// R.id.navigation_data_fields_chooser,
// R.id.navigation_data_fields,
//
// R.id.navigation_transfer,
// R.id.navigation_file_selector,
// R.id.navigation_transfer_selector,
// R.id.navigation_code_generator,
// R.id.navigation_code_scanner,
// R.id.navigation_bluetooth_sender,
// R.id.navigation_bluetooth_receiver,
// R.id.navigation_tba,
//
// R.id.navigation_settings)
// .build();
appBarConfiguration = new AppBarConfiguration.Builder(
R.id.navigation_scouting,
R.id.navigation_data,
R.id.navigation_transfer,
R.id.navigation_settings)
.build();
// appBarConfiguration.set
navController = Navigation.findNavController(this, R.id.nav_host_fragment_activity_main);
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
NavigationUI.setupWithNavController(navView, navController);
navView.setOnItemSelectedListener(new NavigationBarView.OnItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
clearBackStack();
navController.navigate(item.getItemId(), savedInstanceState, new NavOptions.Builder()
.setEnterAnim(R.anim.enter_anim)
.setExitAnim(R.anim.exit_anim)
.setPopEnterAnim(R.anim.pop_enter_anim)
.setPopExitAnim(R.anim.pop_exit_anim).build()
);
return true;
}
});
}
@Override
public boolean onSupportNavigateUp() {
return navController.navigateUp() || super.onSupportNavigateUp();
}
private void clearBackStack() {
navController.popBackStack(navController.getGraph().getStartDestinationId(), false);
}
public interface activityResultRelay {
void onActivityResult(int requestCode, int resultCode, Intent data);
}
public static activityResultRelay resultRelay = null;
public static void setResultRelay(activityResultRelay tmpresultRelay){
resultRelay = tmpresultRelay;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// AlertManager.error(String.valueOf(requestCode));
if (resultRelay != null) {
resultRelay.onActivityResult(resultCode, requestCode, data);
}
}
}
@@ -0,0 +1,9 @@
package com.ridgebotics.ridgescout.SettingsVersionStack;
public class latestSettings {
public static sv1 settings = new sv1();
public static void update(){
settings.init_settings();
settings.update();
}
}
@@ -0,0 +1,110 @@
package com.ridgebotics.ridgescout.SettingsVersionStack;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.fileEditor;
import java.nio.charset.StandardCharsets;
public abstract class settingsVersion {
private static final String settingsFilename = "settings.txt";
public abstract void defaultSettings();
public abstract int getVersion();
public abstract void update();
public static String get_settings_file_content(){
byte[] data = fileEditor.readFile(settingsFilename);
if(data == null){return "";}
return new String(data, StandardCharsets.UTF_8);
}
public int get_file_version(){
String[] fileContent = get_settings_file_content().split("\n");
try{
return Integer.parseInt(fileContent[0]);
}catch(Exception e){
return -1;
}
}
public void set_file_version(int version){
String[] fileContent = get_settings_file_content().split("\n");
String output = String.valueOf(version);
for(int i = 0; i < fileContent.length; i++){
output += ("\n" + fileContent[i]);
}
fileEditor.writeFile(settingsFilename, output.getBytes(StandardCharsets.UTF_8));
}
public String readTag(String search_tag){
String[] fileContent = get_settings_file_content().split("\n");
try{
for(String line : fileContent){
if(line.isEmpty()){
continue;
}
String[] split = line.split("=");
if(split[0].equals(search_tag)){
if(split[1].equals("<empty>")){
return "";
}else if(split[1].equals("<null>")){
return null;
}else {
return split[1];
}
}
}
}catch (Exception e){
AlertManager.error(e);
}
return null;
}
public void init_settings(){
if(!fileEditor.fileExist(settingsFilename)){
fileEditor.createFile(settingsFilename);
set_file_version(getVersion());
defaultSettings();
}
}
public String writeTag(String tag_name, String data){
final boolean already_exists = readTag(tag_name) != null;
if(data == null){
data = "<null>";
}else if(data.equals("")){
data = "<empty>";
}
if(!already_exists){
String fileContent = get_settings_file_content();
String output = fileContent + "\n" + tag_name + "=" + data;
fileEditor.writeFile(settingsFilename, output.getBytes(StandardCharsets.UTF_8));
return output;
}else{
String[] fileContent = get_settings_file_content().split("\n");
try{
for(int i = 0; i < fileContent.length; i++){
if(fileContent[i].isEmpty()){
continue;
}
String[] split = fileContent[i].split("=");
if(split[0].equals(tag_name)){
fileContent[i] = tag_name + "=" + data;
String output = String.join("\n", fileContent);
fileEditor.writeFile(settingsFilename, output.getBytes(StandardCharsets.UTF_8));
return output;
}
}
}catch (Exception e){
e.printStackTrace();
}
}
return "No idea how this happened";
}
}
@@ -0,0 +1,51 @@
package com.ridgebotics.ridgescout.SettingsVersionStack;
public class sv0 extends settingsVersion {
@Override
public int getVersion() {
return 0;
}
@Override
public void update(){
// int file_version = get_file_version();
// if(file_version == getVersion()) {
// return;
// }else if(file_version < getVersion()){
// super.update();
// }
// set_file_version(getVersion());
}
@Override
public void defaultSettings() {
writeTag("username", "Username");
writeTag("selected_event_code", "unset");
// writeTag("practice_mode", "false");
writeTag("wifi_mode", "false");
}
public void set_username(String name){
writeTag("username", name);
}
public String get_username(){
return readTag("username");
}
public void set_evcode(String evcode){
writeTag("selected_event_code", evcode);
}
public String get_evcode(){
return readTag("selected_event_code");
}
// public void set_practice_mode(boolean value) {
// writeTag("practice_mode", value ? "true" : "false");
// }
// public boolean get_practice_mode(){
// return readTag("practice_mode").equals("true");
// }
public void set_wifi_mode(boolean value){
writeTag("wifi_mode", value ? "true" : "false");
}
public boolean get_wifi_mode(){
return readTag("wifi_mode").equals("true");
}
}
@@ -0,0 +1,84 @@
package com.ridgebotics.ridgescout.SettingsVersionStack;
import java.util.UUID;
public class sv1 extends sv0 {
@Override
public int getVersion() {
return 0;
}
@Override
public void update(){
int file_version = get_file_version();
if(file_version == getVersion()) {
return;
}else if(file_version < getVersion()){
super.update();
}
set_file_version(getVersion());
}
@Override
public void defaultSettings() {
writeTag("username", "Username");
writeTag("selected_event_code", "unset");
// writeTag("practice_mode", "false");
writeTag("wifi_mode", "false");
writeTag("team_num", "4388");
writeTag("match_num", "0");
writeTag("alliance_pos", "red-1");
writeTag("data_view_mode", "0");
writeTag("bt_uuid", UUID.randomUUID().toString());
}
public int get_match_num(){
return Integer.parseInt(readTag("match_num"));
}
public void set_match_num(int num){
writeTag("match_num", String.valueOf(num));
}
@Override
public void set_evcode(String evcode){
set_match_num(0);
writeTag("selected_event_code", evcode);
}
public String get_alliance_pos(){
return readTag("alliance_pos");
}
public void set_alliance_pos(String pos){
writeTag("alliance_pos", pos);
}
public int get_team_num(){return Integer.parseInt(readTag("team_num"));}
public void set_team_num(String str){
if(str.isEmpty()) {
set_team_num(0);
return;
}
set_team_num(Integer.parseInt(str));
}
public void set_team_num(int num){writeTag("team_num", String.valueOf(num));}
public int get_data_view_mode(){
return Integer.parseInt(readTag("data_view_mode"));
}
public void set_data_view_mode(int mode){
writeTag("data_view_mode", String.valueOf(mode));
}
public void setUUID(UUID uuid){
writeTag("bt_uuid", uuid.toString());
}
public UUID getUUID(){
return UUID.fromString(readTag("bt_uuid"));
}
}
@@ -0,0 +1,97 @@
package com.ridgebotics.ridgescout.scoutingData;
import com.ridgebotics.ridgescout.scoutingData.transfer.transferType;
import com.ridgebotics.ridgescout.types.ScoutingArray;
import com.ridgebotics.ridgescout.types.data.dataType;
import com.ridgebotics.ridgescout.types.data.stringType;
import com.ridgebotics.ridgescout.types.input.inputType;
import com.ridgebotics.ridgescout.types.data.intType;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.fileEditor;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder;
import java.util.ArrayList;
public class ScoutingDataWriter {
// private static final int int_type_id = 255;
// private static final int string_type_id = 254;
public static boolean save(int version, String username, String filename, dataType[] data){
ByteBuilder bb = new ByteBuilder();
try {
bb.addInt(version);
bb.addString(username);
for(int i = 0; i < data.length; i++){
switch (data[i].getValueType()){
case NUM:
bb.addInt((int) data[i].forceGetValue());
System.out.println("Saved INT: " + data[i].getName() + ", ("+ data[i].get() +")");
break;
case STRING:
bb.addString((String) data[i].forceGetValue());
System.out.println("Saved STR: " + data[i].getName() + ", ("+ data[i].get() +")");
break;
}
}
byte[] bytes = bb.build();
fileEditor.writeFile(filename, bytes);
return true;
} catch (ByteBuilder.buildingException e) {
AlertManager.error(e);
return false;
}
}
public static class ParsedScoutingDataResult {
public String filename;
public String username;
public int version;
public ScoutingArray data;
}
public static ParsedScoutingDataResult load(String filename, inputType[][] values , transferType[][] transferValues){
byte[] bytes = fileEditor.readFile(filename);
BuiltByteParser bbp = new BuiltByteParser(bytes);
try {
ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse();
dataType[] dataTypes = new dataType[objects.size()-2];
int version = ((int)objects.get(0).get());
// System.out.println(version);
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
dataTypes[i] = intType.newNull(values[version][i].name);
dataTypes[i].forceSetValue(objects.get(i+2).get());
System.out.println("Loaded INT: " + values[version][i].name + ", ("+ dataTypes[i].get() +")");
break;
case 2: // String
dataTypes[i] = stringType.newNull(values[version][i].name);
dataTypes[i].forceSetValue(objects.get(i+2).get());
System.out.println("Loaded STR: " + values[version][i].name + ", ("+ dataTypes[i].get() +")");
break;
}
}
ScoutingArray msa = new ScoutingArray(version, dataTypes, 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;
}
}
}
@@ -0,0 +1,148 @@
package com.ridgebotics.ridgescout.scoutingData;
import com.ridgebotics.ridgescout.types.input.dropdownType;
import com.ridgebotics.ridgescout.types.input.inputType;
import com.ridgebotics.ridgescout.types.input.tallyType;
import com.ridgebotics.ridgescout.types.input.textType;
import com.ridgebotics.ridgescout.types.input.sliderType;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.fileEditor;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder;
import java.util.ArrayList;
public class fields {
// public static ScoutingVersion sv = new ScoutingVersion();
public static final String matchFieldsFilename = "matches.fields";
public static final String pitsFieldsFilename = "pits.fields";
public static final inputType[][] default_match_fields = new inputType[][] {
{
new tallyType("Auto Notes", 0),
new sliderType("Auto Performance", 5, 0, 10),
new textType("Auto Comments", ""),
new tallyType("Teleop Notes", 0),
new sliderType("Teleop Performance", 5, 0, 10),
new textType("Teleop Comments", ""),
new sliderType("Overall Driving Performance", 5, 0, 10),
new textType("Overall Driving Comments", ""),
new sliderType("Score area (AMP <-> Speaker)", 5, 0, 10),
new dropdownType("End Condition", new String[]{"Nothing", "Attempted Climb", "Successful Climbed", "Climbed with multiple robots", "Climbed with trap"}, 0),
new dropdownType("Robot Condition", new String[]{"Everything was working", "Something seemed to be broken", "Something was broken", "Missing robot (Joe Johnson)"}, 0),
new textType("Other Comments", "")
}
};
public static final inputType[][] default_pit_fields = new inputType[][] {
{
new sliderType("How good is robot", 5, 0, 10),
new textType("notes", ""),
},{
new sliderType("How good is robot", 5, 0, 10),
new sliderType("Test", 1, 0, 10),
new textType("notes", ""),
}
};
public static boolean save(String filename, inputType[][] values){
try {
ByteBuilder bb = new ByteBuilder();
for (int i = 0; i < values.length; i++) {
bb.addRaw(127, save_version(values[i]));
}
fileEditor.writeFile(filename, bb.build());
return true;
}catch (ByteBuilder.buildingException e) {
AlertManager.error(e);
return false;
// throw new RuntimeException(e);
}
}
private static byte[] save_version(inputType[] values) throws ByteBuilder.buildingException {
ByteBuilder bb = new ByteBuilder();
for(int i =0; i < values.length; i++){
bb.addRaw(values[i].get_byte_id(), values[i].encode());
}
return bb.build();
}
public static inputType[][] load(String filename){
byte[] bytes = fileEditor.readFile(filename);
// System.out.println(bytes);
try {
BuiltByteParser bbp = new BuiltByteParser(bytes);
ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse();
inputType[][] values = new inputType[objects.size()][];
for(int i = 0 ; i < objects.size(); i++){
values[i] = load_version((byte[]) objects.get(i).get());
}
return values;
} catch (Exception e) {
AlertManager.error(e);
return null;
}
}
private static inputType[] load_version(byte[] bytes) throws BuiltByteParser.byteParsingExeption{
BuiltByteParser bbp = new BuiltByteParser(bytes);
ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse();
inputType[] output = new inputType[objects.size()];
for(int i = 0 ; i < objects.size(); i++){
BuiltByteParser.parsedObject obj = objects.get(i);
inputType t = null;
switch (obj.getType()){
case inputType.slider_type_id:
t = new sliderType();
break;
case inputType.dropdownType:
t = new dropdownType();
break;
case inputType.notesType:
t = new textType();
break;
case inputType.tallyType:
t = new tallyType();
break;
}
t.decode((byte[]) obj.get());
output[i] = t;
}
return output;
}
// public static void test(){
// ScoutingVersion.transferType[][] transferValues = sv.get_transfer_values(values);
//
// ScoutingVersion.ScoutingArray msa = sv.new ScoutingArray(0, new ScoutingVersion.dataType[]{
// sv.new stringType("name", "test-username"),
// sv.new intType("How good is robot", 12)
// }, values, transferValues);
//
// msa.update();
//
// for(ScoutingVersion.dataType dt : msa.array){
// if(dt == null) continue;
// switch (dt.getValueType()){
// case NUM:
// System.out.println(dt.name + " " + (int) dt.get());
// break;
// case STRING:
// System.out.println(dt.name + " " + (String) dt.get());
// break;
// }
//
// }
// }
}
@@ -0,0 +1,8 @@
package com.ridgebotics.ridgescout.scoutingData.transfer;
public class createTransferType extends transferType {
public transferValue getType() {return transferValue.CREATE;}
public createTransferType(String name){
super(name);
}
}
@@ -0,0 +1,8 @@
package com.ridgebotics.ridgescout.scoutingData.transfer;
public class directTransferType extends transferType {
public transferValue getType() {return transferValue.DIRECT;}
public directTransferType(String name){
super(name);
}
}
@@ -0,0 +1,41 @@
package com.ridgebotics.ridgescout.scoutingData.transfer;
import com.ridgebotics.ridgescout.types.input.inputType;
public abstract class transferType {
public enum transferValue {
DIRECT,
CREATE
}
public String name;
public abstract transferValue getType();
public transferType(String name){
this.name = name;
}
private static inputType get_input_type_by_name(inputType[] values, String name){
for(inputType it : values){
if(it.name.equals(name)){
return it;
}
}
return null;
}
public static transferType[][] get_transfer_values(inputType[][] values) {
transferType[][] output = new transferType[values.length][];
for(int a = 1; a < values.length; a++){
transferType[] v = new transferType[values[a].length];
for(int b = 0; b < values[a].length; b++){
String name = values[a][b].name;
if(get_input_type_by_name(values[a-1], name) != null){
v[b] = new directTransferType(name);
}else{
v[b] = new createTransferType(name);
}
}
output[a-1] = v;
}
return output;
}
}
@@ -0,0 +1,96 @@
package com.ridgebotics.ridgescout.types;
import com.ridgebotics.ridgescout.scoutingData.transfer.createTransferType;
import com.ridgebotics.ridgescout.scoutingData.transfer.directTransferType;
import com.ridgebotics.ridgescout.scoutingData.transfer.transferType;
import com.ridgebotics.ridgescout.types.data.dataType;
import com.ridgebotics.ridgescout.types.data.intType;
import com.ridgebotics.ridgescout.types.data.stringType;
import com.ridgebotics.ridgescout.types.input.inputType;
public class ScoutingArray {
public int version;
public dataType[] array;
public inputType[][] values;
public int latest_version_num;
public transferType[][] transfer_values;
public ScoutingArray(int version, dataType[] array, inputType[][] values, transferType[][] transfer_values){
this.version = version;
this.array = array;
this.values = values;
this.latest_version_num = values.length-1;
this.transfer_values = transfer_values;
}
public ScoutingArray(int version, dataType[] array, inputType[][] values){
this(version, array, values, transferType.get_transfer_values(values));
}
public void update(){
while(version<latest_version_num){
dataType[] new_values = new dataType[transfer_values[version].length];
for(int i = 0; i < transfer_values[version].length; i++){
transferType tv = transfer_values[version][i];
switch (tv.getType()){
case DIRECT:
new_values[i] = direct_transfer((directTransferType) tv);
continue;
// case RENAME:
// new_values[i] = rename_transfer((renameTransferType) tv);
// continue;
case CREATE:
new_values[i] = create_transfer((createTransferType) tv);
continue;
}
}
this.array = new_values;
version++;
System.out.println("Updated to " + version);
}
}
private inputType get_input_type_by_name(int version, String name){
for(inputType it : values[version]){
if(it.name.equals(name)){
return it;
}
}
return null;
}
private dataType get_data_type_by_name(String name){
for(dataType dt : array){
if(dt.getName().equals(name)){
return dt;
}
}
return null;
}
private dataType direct_transfer(directTransferType tv){
return get_data_type_by_name(tv.name);
}
// private dataType rename_transfer(renameTransferType tv){
// dataType dt = get_data_type_by_name(tv.name);
// dt.name = tv.new_name;
// return dt;
// }
private dataType create_transfer(createTransferType tv){
inputType it = get_input_type_by_name(version+1, tv.name);
switch (it.getValueType()){
case NUM:
return intType.newNull(it.name);
case STRING:
return stringType.newNull(it.name);
}
return null;
}
}
@@ -0,0 +1,31 @@
package com.ridgebotics.ridgescout.types.data;
public abstract class dataType {
public enum valueTypes {
NUM,
STRING
}
private Object value;
private final String name;
public abstract valueTypes getValueType();
public Object forceGetValue(){return value;}
public void forceSetValue(Object value){this.value = value;}
public abstract Object get();
public abstract void set(Object value);
// public abstract Object getNullValue();
// public abstract Object getUnselectedValue();
public abstract boolean isNull();
// public abstract boolean isUnselected();
public String getName() {return name;}
public dataType(String name){
this.name = name;
}
}
@@ -0,0 +1,56 @@
package com.ridgebotics.ridgescout.types.data;
public class intType extends dataType {
public static final int nullval = 255;
// public static final int unselectedval = 1;
public valueTypes getValueType() {
return valueTypes.NUM;
}
// public Object getNullValue(){
// return nullval;
// }
// public Object getUnselectedValue(){
// return unselectedval;
// }
public Object get(){
return (int) forceGetValue();
}
public void set(Object value){
forceSetValue((int) value);
}
public intType(String name, int value) {
super(name);
set(value);
}
public static intType newNull(String name){
final intType a = new intType(name, 0);
a.forceSetValue(nullval);
return a;
}
// public static intType newUnselected(String name){
// final intType a = new intType(name, 0);
// a.forceSetValue(unselectedval);
// return a;
// }
public static boolean isNull(int obj){
return obj == nullval;
}
public boolean isNull() {
return isNull((int) forceGetValue());
}
// public static boolean isUnselected(int obj){
// return obj == unselectedval;
// }
// public boolean isUnselected() {
// return isUnselected((int) forceGetValue());
// }
}
@@ -0,0 +1,57 @@
package com.ridgebotics.ridgescout.types.data;
public class stringType extends dataType{
public static final String nullval = "null";
// public static final String unselectedval = "■";
public valueTypes getValueType() {
return valueTypes.STRING;
}
// public Object getNullValue(){
// return nullval;
// }
// public Object getUnselectedValue(){
// return unselectedval;
// }
public Object get(){
return forceGetValue();
}
public void set(Object value){
forceSetValue(value);
}
public stringType(String name, String value) {
super(name);
forceSetValue(value);
}
public static stringType newNull(String name){
final stringType a = new stringType(name, "");
a.forceSetValue(nullval);
return a;
}
// public static stringType newUnselected(String name){
// final stringType a = new stringType(name, "");
// a.forceSetValue(unselectedval);
// return a;
// }
public static boolean isNull(String obj){
return obj.equals(nullval);
}
public boolean isNull() {
return isNull((String) forceGetValue());
}
// public static boolean isUnselected(String obj){
// return obj.equals(unselectedval);
// }
// public boolean isUnselected() {
// return isUnselected((String) forceGetValue());
// }
}
@@ -0,0 +1,63 @@
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.util.ArrayList;
import java.util.Objects;
public class file {
public static final int typecode = 255;
public String filename;
public byte[] data;
public file(){}
public file(String filename){
this(filename, fileEditor.readFile(filename));
}
public file(String filename, byte[] data){
this.filename = filename;
this.data = data;
}
public byte[] encode(){
try {
ByteBuilder bb = new ByteBuilder()
.addString(filename)
.addRaw(255, Objects.requireNonNull(fileEditor.readFile(filename)));
return bb.build();
} catch (ByteBuilder.buildingException e) {
AlertManager.error(e);
return null;
}
}
public static file decode(byte[] bytes){
try{
ArrayList<BuiltByteParser.parsedObject> objects = new BuiltByteParser(bytes).parse();
file f = new file();
f.filename = (String) objects.get(0).get();
f.data = (byte[]) objects.get(1).get();
return f;
}catch (BuiltByteParser.byteParsingExeption e){
AlertManager.error(e);
return null;
}
}
public boolean write(){
if(data == null || filename == null) return false;
return fileEditor.writeFile(filename, data);
}
}
@@ -0,0 +1,79 @@
package com.ridgebotics.ridgescout.types;
import androidx.annotation.NonNull;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder;
import java.util.ArrayList;
public class frcEvent {
public static final int typecode = 254;
public String eventCode;
public String name;
public ArrayList<frcMatch> matches;
public ArrayList<frcTeam> teams;
public byte[] encode() {
try {
ByteBuilder bb = new ByteBuilder()
.addString(eventCode)
.addString(name);
for (frcTeam teams : teams) {
bb.addRaw(frcTeam.typecode, teams.encode());
}
for (frcMatch match : matches) {
bb.addRaw(frcMatch.typecode, match.encode());
}
return bb.build();
} catch (ByteBuilder.buildingException e) {
AlertManager.error(e);
return null;
}
}
public static frcEvent decode(byte[] bytes) {
try {
ArrayList<BuiltByteParser.parsedObject> objects =
new BuiltByteParser(bytes).parse();
frcEvent frc = new frcEvent();
frc.eventCode = (String) objects.get(0).get();
frc.name = (String) objects.get(1).get();
frc.matches = new ArrayList<>();
frc.teams = new ArrayList<>();
for (BuiltByteParser.parsedObject object : objects) {
if (object.getType() == frcTeam.typecode) {
frc.teams.add(frcTeam.decode((byte[]) object.get()));
} else if (object.getType() == frcMatch.typecode) {
frc.matches.add(frcMatch.decode((byte[]) object.get()));
}
}
return frc;
} catch (BuiltByteParser.byteParsingExeption e) {
AlertManager.error(e);
return null;
}
}
@NonNull
public String toString() {
return (
"frcEvent Name: " +
name +
", Code: " +
eventCode +
" numTeams: " +
teams.size() +
" numMatches: " +
matches.size()
);
}
}
@@ -0,0 +1,63 @@
package com.ridgebotics.ridgescout.types;
import androidx.annotation.NonNull;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder;
import java.util.ArrayList;
import java.util.Arrays;
public class frcMatch {
public static final int typecode = 253;
public frcMatch(){}
public int matchIndex = 0;
public int[] blueAlliance = new int[3];
public int[] redAlliance = new int[3];
public byte[] encode(){
try {
return new ByteBuilder()
.addInt(matchIndex)
.addInt(blueAlliance[0])
.addInt(blueAlliance[1])
.addInt(blueAlliance[2])
.addInt(redAlliance[0])
.addInt(redAlliance[1])
.addInt(redAlliance[2])
.build();
} catch (ByteBuilder.buildingException e) {
AlertManager.error(e);
return new byte[1];
}
}
public static frcMatch decode(byte[] bytes){
try {
ArrayList<BuiltByteParser.parsedObject> objects = new BuiltByteParser(bytes).parse();
frcMatch frc = new frcMatch();
frc.matchIndex = (int) objects.get(0).get();
frc.blueAlliance[0] = (int) objects.get(1).get();
frc.blueAlliance[1] = (int) objects.get(2).get();
frc.blueAlliance[2] = (int) objects.get(3).get();
frc.redAlliance[0] = (int) objects.get(4).get();
frc.redAlliance[1] = (int) objects.get(5).get();
frc.redAlliance[2] = (int) objects.get(6).get();
return frc;
} catch (BuiltByteParser.byteParsingExeption e) {
AlertManager.error(e);
return null;
}
}
@NonNull
public String toString(){
return "frcMatch Num: " + matchIndex + ", Blue: " + Arrays.toString(blueAlliance) + ", Red: " + Arrays.toString(redAlliance);
}
}
@@ -0,0 +1,67 @@
package com.ridgebotics.ridgescout.types;
import androidx.annotation.NonNull;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder;
import java.util.ArrayList;
public class frcTeam {
public static final int typecode = 252;
public int teamNumber = 0;
public String teamName = "null";
public String city = "null";
public String stateOrProv = "null";
public String school = "null";
public String country = "null";
public int startingYear = 0;
public String getDescription(){
return teamName + " Started in " + startingYear + ", and are from " + school + " in " + city + ", " + stateOrProv + ", " + country;
}
public byte[] encode(){
try {
return new ByteBuilder()
.addInt(teamNumber)
.addString(teamName)
.addString(city)
.addString(stateOrProv)
.addString(school)
.addString(country)
.addInt(startingYear)
.build();
} catch (ByteBuilder.buildingException e) {
AlertManager.error(e);
return null;
}
}
public static frcTeam decode(byte[] bytes){
try {
ArrayList<BuiltByteParser.parsedObject> objects = new BuiltByteParser(bytes).parse();
frcTeam frc = new frcTeam();
frc.teamNumber = (int) objects.get(0).get();
frc.teamName = (String) objects.get(1).get();
frc.city = (String) objects.get(2).get();
frc.stateOrProv = (String) objects.get(3).get();
frc.school = (String) objects.get(4).get();
frc.country = (String) objects.get(5).get();
frc.startingYear = (int) objects.get(6).get();
return frc;
} catch (BuiltByteParser.byteParsingExeption e) {
AlertManager.error(e);
return null;
}
}
@NonNull
public String toString(){
return "frcTeam Num: " + teamNumber + ", " + getDescription();
}
}
@@ -0,0 +1,279 @@
package com.ridgebotics.ridgescout.types.input;
import android.content.Context;
import android.graphics.Color;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
import com.ridgebotics.ridgescout.types.data.dataType;
import com.ridgebotics.ridgescout.types.data.intType;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder;
import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.charts.PieChart;
import com.github.mikephil.charting.components.Legend;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.data.LineDataSet;
import com.github.mikephil.charting.data.PieData;
import com.github.mikephil.charting.data.PieDataSet;
import com.github.mikephil.charting.data.PieEntry;
import com.skydoves.powerspinner.IconSpinnerAdapter;
import com.skydoves.powerspinner.IconSpinnerItem;
import com.skydoves.powerspinner.OnSpinnerItemSelectedListener;
import com.skydoves.powerspinner.PowerSpinnerView;
import com.skydoves.powerspinner.SpinnerGravity;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
public class dropdownType extends inputType {
public String[] text_options;
public int get_byte_id() {return dropdownType;}
public inputTypes getInputType(){return inputTypes.DROPDOWN;}
public dataType.valueTypes getValueType(){return dataType.valueTypes.NUM;}
public Object get_fallback_value(){return 0;}
public dropdownType(){};
public String get_type_name(){return "Dropdown";}
public dropdownType(String name, String[] text_options, int defaultSelIndex){
super(name);
this.text_options = text_options;
this.default_value = defaultSelIndex;
}
public byte[] encode() throws ByteBuilder.buildingException {
ByteBuilder bb = new ByteBuilder();
bb.addString(name);
bb.addInt((int)default_value);
bb.addStringArray(text_options);
return bb.build();
}
public void decode(byte[] bytes) throws BuiltByteParser.byteParsingExeption {
BuiltByteParser bbp = new BuiltByteParser(bytes);
ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse();
name = (String) objects.get(0).get();
default_value = objects.get(1).get();
text_options = (String[]) objects.get(2).get();
}
public PowerSpinnerView dropdown = null;
public View createView(Context context, Function<dataType, Integer> onUpdate){
dropdown = new PowerSpinnerView(context);
List<IconSpinnerItem> iconSpinnerItems = new ArrayList<>();
for(int i = 0; i < text_options.length; i++){
iconSpinnerItems.add(new IconSpinnerItem(text_options[i]));
}
IconSpinnerAdapter iconSpinnerAdapter = new IconSpinnerAdapter(dropdown);
dropdown.setGravity(Gravity.CENTER);
dropdown.setSpinnerAdapter(iconSpinnerAdapter);
dropdown.setItems(iconSpinnerItems);
dropdown.selectItemByIndex((int) default_value);
dropdown.setPadding(10,20,10,20);
dropdown.setBackgroundColor(0xf0000000);
dropdown.setTextColor(0xff00ff00);
dropdown.setTextSize(14.5f);
dropdown.setArrowGravity(SpinnerGravity.END);
dropdown.setArrowPadding(8);
// dropdown.setSpinnerItemHeight(46);
dropdown.setSpinnerPopupElevation(14);
dropdown.setOnSpinnerItemSelectedListener(new OnSpinnerItemSelectedListener<IconSpinnerItem>() {
@Override
public void onItemSelected(int oldIndex, @Nullable IconSpinnerItem oldItem, int newIndex,
IconSpinnerItem newItem) {
onUpdate.apply(getViewValue());
}
});
// dropdown.setLifecycleOwner(context.life);
// slider.addOnChangeListener(new Slider.OnChangeListener() {
// @Override
// public void onValueChange(@NonNull Slider slider, float value, boolean fromUser) {
// onUpdate.apply(getViewValue());
// }
// });
return dropdown;
}
public void setViewValue(Object value) {
if(dropdown == null) return;
if(intType.isNull((int) value)){
nullify();
return;
}
isBlank = false;
dropdown.setVisibility(View.VISIBLE);
dropdown.selectItemByIndex((int) value);
}
public void nullify(){
isBlank = true;
dropdown.setVisibility(View.GONE);
}
public dataType getViewValue(){
if(dropdown == null) return null;
if(dropdown.getVisibility() == View.GONE) return new intType(name, intType.nullval);
return new intType(name, dropdown.getSelectedIndex());
}
public void add_individual_view(LinearLayout parent, dataType 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.setPadding(20,20,20,20);
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText(text_options[(int) data.get()]);
tv.setTextSize(18);
parent.addView(tv);
}
private static int[] generateEquidistantColors(int N) {
int[] colors = new int[N];
float[] hsv = new float[3]; // Hue, Saturation, Value
for (int i = 0; i < N; i++) {
float hue = i * 1.0F / N;
hsv[0] = hue * 360; // Convert hue to degrees (0 to 360)
hsv[1] = 1; // Maximum saturation
hsv[2] = 1; // Maximum brightness (value)
colors[i] = Color.HSVToColor(hsv);
}
return colors;
}
public void add_compiled_view(LinearLayout parent, dataType[] data){
PieChart chart = new PieChart(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);
parent.addView(chart);
int[] data_2 = new int[text_options.length];
for(int i = 0; i < data.length; i++)
if(!data[i].isNull())
data_2[(int) data[i].get()]++;
List<PieEntry> entries = new ArrayList<>();
for(int i = 0; i < data_2.length; i++) {
PieEntry entry = new PieEntry((float) data_2[i], text_options[i]);
entries.add(entry);
}
PieDataSet pieDataSet = new PieDataSet(entries, name);
pieDataSet.setColors(generateEquidistantColors(text_options.length));
PieData pieData = new PieData(pieDataSet);
chart.setDrawHoleEnabled(false);
chart.setData(pieData);
}
public void add_history_view(LinearLayout parent, dataType[] data){
LineChart chart = new LineChart(parent.getContext());
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
layout.height = 350;
chart.setLayoutParams(layout);
chart.setBackgroundColor(0xff252025);
int[] colors = generateEquidistantColors(text_options.length);
LineData lineData = new LineData();
for(int i = 0; i < text_options.length; i++){
List<Entry> entries = new ArrayList<>();
for (int a = 0; a < data.length; a++) {
if(data[a].isNull()) continue;
entries.add(
new Entry(a,
((int) data[a].get()) == i ? 1.f : 0.f
)
);
}
LineDataSet dataSet = new LineDataSet(entries, text_options[i]);
dataSet.setColor(colors[i]);
dataSet.setValueTextColor(Color.BLACK);
dataSet.setDrawCircles(false);
dataSet.setDrawValues(false);
dataSet.setValueTextColor(Color.RED);
lineData.addDataSet(dataSet);
}
chart.setData(lineData);
chart.invalidate();
chart.getDescription().setEnabled(false);
chart.setTouchEnabled(false);
chart.setDragEnabled(false);
chart.setScaleEnabled(false);
chart.getXAxis().setTextColor(Color.WHITE);
chart.getAxisLeft().setTextColor(Color.WHITE);
chart.getAxisRight().setTextColor(Color.WHITE);
chart.getAxisLeft().setAxisMinimum(0.f);
chart.getAxisLeft().setAxisMaximum(1.f);
chart.getAxisRight().setAxisMinimum(0.f);
chart.getAxisRight().setAxisMaximum(1.f);
Legend legend = chart.getLegend();
legend.setTextColor(Color.WHITE);
chart.invalidate();
parent.addView(chart);
}
}
@@ -0,0 +1,100 @@
package com.ridgebotics.ridgescout.types.input;
import android.content.Context;
import android.view.View;
import android.widget.LinearLayout;
import com.ridgebotics.ridgescout.types.data.dataType;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder;
import java.util.function.Function;
public abstract class inputType {
public static final int slider_type_id = 255;
public static final int dropdownType = 254;
public static final int notesType = 253;
public static final int tallyType = 252;
public enum inputTypes {
SLIDER,
DROPDOWN,
NOTES_INPUT,
TALLY
}
public String name;
public Object default_value;
public abstract inputTypes getInputType();
public abstract dataType.valueTypes getValueType();
public abstract Object get_fallback_value();
public abstract int get_byte_id();
public inputType(){}
public inputType(String name){
this.name = name;
}
public abstract String get_type_name();
public abstract byte[] encode() throws ByteBuilder.buildingException;
public abstract void decode(byte[] bytes) throws BuiltByteParser.byteParsingExeption;
// public abstract dataType[] getConfig();
// public abstract void setConfig(dataType[] config);
public abstract View createView(Context context, Function<dataType, Integer> onUpdate);
public boolean isBlank = false;
public abstract void nullify();
public void setViewValue(dataType type){setViewValue(type.get());}
public abstract void setViewValue(Object value);
public abstract dataType getViewValue();
// private enum parameterTypeEnum {
// paramNumber,
// paramString,
// paramStringArray
// }
//
// public static class parameterType {
// public String name;
// public parameterTypeEnum id;
// }
//
// public static class paramNumber extends parameterType {
// public int val;
// public paramNumber(String name, int val){
// this.name = name + " (Number)";
// this.val = val;
// this.id = parameterTypeEnum.paramNumber;
// }
// }
//
// public static class paramString extends parameterType {
// public String val;
// public paramString(String name, String val){
// this.name = name + " (String)";
// this.val = val;
// this.id = parameterTypeEnum.paramString;
// }
// }
//
// public static class paramStringArray extends parameterType {
// public String[] val;
// public paramStringArray(String name, String[] val){
// this.name = name + " (String array)";
// this.val = val;
// this.id = parameterTypeEnum.paramStringArray;
// }
// }
// public abstract parameterType[] getDefaultParameters();
public abstract void add_individual_view(LinearLayout parent, dataType data);
public abstract void add_compiled_view(LinearLayout parent, dataType[] data);
public abstract void add_history_view(LinearLayout parent, dataType[] data);
}
@@ -0,0 +1,305 @@
package com.ridgebotics.ridgescout.types.input;
import android.content.Context;
import android.graphics.Color;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import com.ridgebotics.ridgescout.types.data.dataType;
import com.ridgebotics.ridgescout.types.data.intType;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder;
import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.components.Legend;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.data.LineDataSet;
import com.google.android.material.slider.Slider;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
public class sliderType extends inputType {
// public int defaultValue;
public int min;
public int max;
public int get_byte_id() {return slider_type_id;}
public inputTypes getInputType(){return inputTypes.SLIDER;}
public dataType.valueTypes getValueType(){return dataType.valueTypes.NUM;}
public Object get_fallback_value(){return 0;}
public sliderType(){};
public String get_type_name(){return "Slider";}
public sliderType(String name, int defaultValue, int min, int max){
super(name);
this.default_value = defaultValue;
this.min = min;
this.max = max;
}
public byte[] encode() throws ByteBuilder.buildingException {
ByteBuilder bb = new ByteBuilder();
bb.addString(name);
bb.addInt((int)default_value);
bb.addInt(min);
bb.addInt(max);
return bb.build();
}
public void decode(byte[] bytes) throws BuiltByteParser.byteParsingExeption {
BuiltByteParser bbp = new BuiltByteParser(bytes);
ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse();
name = (String) objects.get(0).get();
default_value = objects.get(1).get();
min = (int) objects.get(2).get();
max = (int) objects.get(3).get();
}
public Slider slider = null;
public View createView(Context context, Function<dataType, Integer> onUpdate){
slider = new Slider(context);
setViewValue(default_value);
slider.setStepSize((float) 1 / (max-min));
slider.addOnChangeListener(new Slider.OnChangeListener() {
@Override
public void onValueChange(@NonNull Slider slider, float value, boolean fromUser) {
onUpdate.apply(getViewValue());
}
});
return slider;
}
public void setViewValue(Object value) {
if(slider == null) return;
if(intType.isNull((int) value)){
nullify();
return;
}
float slider_position = (float) ((int) value-min) / (max-min);
float step_size = (float) 1/(max-min);
int round_position = Math.round(slider_position / step_size);
isBlank = false;
float slidervalue = round_position*step_size;
if(slidervalue > 1 || slidervalue < 0) {
AlertManager.error("Error loading slider " + name);
slider.setValue(0);
}else{
slider.setValue(slidervalue);
}
slider.setVisibility(View.VISIBLE);
}
public dataType getViewValue(){
if(slider == null) return null;
if(slider.getVisibility() == View.GONE) return intType.newNull(name);
return new intType(name, min + (int) (slider.getValue() * (max-min)));
}
public void nullify(){
isBlank = true;
slider.setVisibility(View.GONE);
}
public void add_individual_view(LinearLayout parent, dataType data){
if(data.isNull()) return;
Slider slider = new Slider(parent.getContext());
float slider_position = (float) ((int) data.get()-min) / (max-min);
float step_size = (float) 1/(max-min);
int round_position = Math.round(slider_position / step_size);
float value = round_position*step_size;
if(value > 1 || value < 0) {
AlertManager.error("Error loading slider " + name);
slider.setValue(0);
}else{
slider.setValue(value);
slider.setStepSize((float) 1 / (max-min));
}
slider.setEnabled(false);
parent.addView(slider);
}
private float calculateMean(int[] data) {
float sum = 0;
for (int value : data) {
sum += (float) value;
}
return sum / data.length;
}
private float calculateStandardDeviation(int[] data, float mean) {
float sum = 0;
for (int value : data) {
sum += Math.pow((float) value - mean, 2);
}
return (float) Math.sqrt(sum / (data.length - 1));
}
private List<Entry> generateNormalDistribution(float mean, float stdDev, int count, int scale) {
List<Entry> entries = new ArrayList<>();
for (int i = 0; i < count; i++) {
float x = i;
float y = (float) ((1 / (stdDev * Math.sqrt(2 * Math.PI)))
* Math.exp(-0.5 * Math.pow((x - mean) / stdDev, 2)));
entries.add(new Entry(x, y*scale)); // Scale y for visibility
}
return entries;
}
public void add_compiled_view(LinearLayout parent, dataType[] data){
LineChart chart = new LineChart(parent.getContext());
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
layout.height = 350;
chart.setLayoutParams(layout);
chart.setBackgroundColor(0xff252025);
int[] values = new int[max-min+1];
for (int i = 0; i < data.length; i++)
if(!data[i].isNull())
values[(int) data[i].get()-min]++;
ArrayList<Integer> mean_temp = new ArrayList<>();
for (int i = 0; i < data.length; i++)
if(!data[i].isNull())
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(Color.BLUE);
dataSet.setValueTextColor(Color.BLACK);
dataSet.setDrawCircles(false);
dataSet.setDrawValues(false);
// Calculate mean and standard deviation
float mean = calculateMean(mean_vals);
float stdDev = calculateStandardDeviation(mean_vals, mean);
// Generate normal distribution curve
List<Entry> normalDistEntries = generateNormalDistribution(mean-min, stdDev, max-min+1, (max-min)/data.length);
LineDataSet normalDistSet = new LineDataSet(normalDistEntries, "Normal Distribution");
normalDistSet.setColor(Color.RED);
normalDistSet.setDrawCircles(false);
normalDistSet.setDrawValues(false);
normalDistSet.setLineWidth(2f);
LineData lineData = new LineData(dataSet, normalDistSet);
chart.setData(lineData);
chart.invalidate();
chart.getDescription().setEnabled(false);
chart.setTouchEnabled(false);
chart.setDragEnabled(false);
chart.setScaleEnabled(false);
dataSet.setValueTextColor(Color.RED);
chart.getXAxis().setTextColor(Color.WHITE);
chart.getAxisLeft().setTextColor(Color.WHITE);
chart.getAxisRight().setTextColor(Color.WHITE);
Legend legend = chart.getLegend();
legend.setTextColor(Color.WHITE);
parent.addView(chart);
}
public void add_history_view(LinearLayout parent, dataType[] data){
LineChart chart = new LineChart(parent.getContext());
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
layout.height = 350;
chart.setLayoutParams(layout);
chart.setBackgroundColor(0xff252025);
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(Color.BLUE);
dataSet.setValueTextColor(Color.BLACK);
dataSet.setDrawCircles(false);
dataSet.setDrawValues(false);
LineData lineData = new LineData(dataSet);
chart.setData(lineData);
chart.invalidate();
chart.getDescription().setEnabled(false);
chart.setTouchEnabled(false);
chart.setDragEnabled(false);
chart.setScaleEnabled(false);
dataSet.setValueTextColor(Color.RED);
chart.getXAxis().setTextColor(Color.WHITE);
chart.getAxisLeft().setTextColor(Color.WHITE);
chart.getAxisRight().setTextColor(Color.WHITE);
Legend legend = chart.getLegend();
legend.setTextColor(Color.WHITE);
chart.getAxisLeft().setAxisMinimum(min);
chart.getAxisLeft().setAxisMaximum(max);
chart.getAxisRight().setAxisMinimum(min);
chart.getAxisRight().setAxisMaximum(max);
parent.addView(chart);
}
}
@@ -0,0 +1,300 @@
package com.ridgebotics.ridgescout.types.input;
import android.content.Context;
import android.graphics.Color;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.ridgebotics.ridgescout.types.data.dataType;
import com.ridgebotics.ridgescout.types.data.intType;
import com.ridgebotics.ridgescout.ui.scouting.TallyCounterView;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder;
import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.components.Legend;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.data.LineDataSet;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
public class tallyType extends inputType {
public int get_byte_id() {return tallyType;}
public inputTypes getInputType(){return inputTypes.TALLY;}
public dataType.valueTypes getValueType(){return dataType.valueTypes.NUM;}
public Object get_fallback_value(){return 0;}
public tallyType(){}
public String get_type_name(){return "Tally";}
public tallyType(String name, int default_value){
super(name);
this.default_value = default_value;
}
public byte[] encode() throws ByteBuilder.buildingException {
ByteBuilder bb = new ByteBuilder();
bb.addString(name);
bb.addInt((int)default_value);
return bb.build();
}
public void decode(byte[] bytes) throws BuiltByteParser.byteParsingExeption {
BuiltByteParser bbp = new BuiltByteParser(bytes);
ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse();
name = (String) objects.get(0).get();
default_value = objects.get(1).get();
}
public TallyCounterView tally = null;
public View createView(Context context, Function<dataType, Integer> onUpdate){
tally = new TallyCounterView(context);
tally.setOnCountChangedListener(n -> onUpdate.apply(getViewValue()));
setViewValue(default_value);
return tally;
}
public void setViewValue(Object value) {
if(tally == null) return;
System.out.println(value);
if(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 dataType 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, dataType 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(dataType[] 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(dataType[] 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, dataType[] data){
LineChart chart = new LineChart(parent.getContext());
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
layout.height = 350;
chart.setLayoutParams(layout);
chart.setBackgroundColor(0xff252025);
int min = findMin(data);
int max = findMax(data);
int[] values = new int[max-min+1];
for (int i = 0; i < data.length; i++)
if(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(Color.BLUE);
dataSet.setValueTextColor(Color.BLACK);
dataSet.setDrawCircles(false);
dataSet.setDrawValues(false);
// Calculate mean and standard deviation
float mean = calculateMean(mean_vals);
float stdDev = calculateStandardDeviation(mean_vals, mean);
// Generate normal distribution curve
List<Entry> normalDistEntries = generateNormalDistribution(mean-min, stdDev, max-min+1, (max-min)/data.length);
LineDataSet normalDistSet = new LineDataSet(normalDistEntries, "Normal Distribution");
normalDistSet.setColor(Color.RED);
normalDistSet.setDrawCircles(false);
normalDistSet.setDrawValues(false);
normalDistSet.setLineWidth(2f);
LineData lineData = new LineData(dataSet, normalDistSet);
chart.setData(lineData);
chart.invalidate();
chart.getDescription().setEnabled(false);
chart.setTouchEnabled(false);
chart.setDragEnabled(false);
chart.setScaleEnabled(false);
dataSet.setValueTextColor(Color.RED);
chart.getXAxis().setTextColor(Color.WHITE);
chart.getAxisLeft().setTextColor(Color.WHITE);
chart.getAxisRight().setTextColor(Color.WHITE);
Legend legend = chart.getLegend();
legend.setTextColor(Color.WHITE);
parent.addView(chart);
}
public void add_history_view(LinearLayout parent, dataType[] data){
LineChart chart = new LineChart(parent.getContext());
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
layout.height = 350;
chart.setLayoutParams(layout);
chart.setBackgroundColor(0xff252025);
int min = findMin(data);
int max = findMax(data);
List<Entry> entries = new ArrayList<>();
for (int i = 0; i < data.length; i++){
if(data[i] == null) continue;
if(data[i].isNull()) continue;
entries.add(new Entry(i, (float)(int) data[i].get()));
}
LineDataSet dataSet = new LineDataSet(entries, name);
dataSet.setColor(Color.BLUE);
dataSet.setValueTextColor(Color.BLACK);
dataSet.setDrawCircles(false);
dataSet.setDrawValues(false);
LineData lineData = new LineData(dataSet);
chart.setData(lineData);
chart.invalidate();
chart.getDescription().setEnabled(false);
chart.setTouchEnabled(false);
chart.setDragEnabled(false);
chart.setScaleEnabled(false);
dataSet.setValueTextColor(Color.RED);
chart.getXAxis().setTextColor(Color.WHITE);
chart.getAxisLeft().setTextColor(Color.WHITE);
chart.getAxisRight().setTextColor(Color.WHITE);
Legend legend = chart.getLegend();
legend.setTextColor(Color.WHITE);
chart.getAxisLeft().setAxisMinimum(min);
chart.getAxisLeft().setAxisMaximum(max);
chart.getAxisRight().setAxisMinimum(min);
chart.getAxisRight().setAxisMaximum(max);
parent.addView(chart);
}
}
@@ -0,0 +1,231 @@
package com.ridgebotics.ridgescout.types.input;
import android.content.Context;
import android.graphics.Color;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.ridgebotics.ridgescout.types.data.dataType;
import com.ridgebotics.ridgescout.types.data.stringType;
import com.ridgebotics.ridgescout.utility.SentimentAnalysis;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder;
import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.components.Legend;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.data.LineDataSet;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
public class textType extends inputType {
public int get_byte_id() {return notesType;}
public inputTypes getInputType(){return inputTypes.NOTES_INPUT;}
public dataType.valueTypes getValueType(){return dataType.valueTypes.STRING;}
public Object get_fallback_value(){return "<no-notes>";}
public textType(){}
public textType(String name, String default_text){
super(name);
this.default_value = default_text;
}
public String get_type_name(){return "Text";}
public byte[] encode() throws ByteBuilder.buildingException {
ByteBuilder bb = new ByteBuilder();
bb.addString(name);
bb.addString((String) default_value);
return bb.build();
}
public void decode(byte[] bytes) throws BuiltByteParser.byteParsingExeption {
BuiltByteParser bbp = new BuiltByteParser(bytes);
ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse();
name = (String) objects.get(0).get();
default_value = objects.get(1).get();
}
public EditText text = null;
public View createView(Context context, Function<dataType, Integer> onUpdate){
text = new EditText(context);
text.setText((String)default_value);
text.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
onUpdate.apply(getViewValue()); }
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
public void onTextChanged(CharSequence s, int start, int before, int count) {}
});
return text;
}
public void setViewValue(Object value) {
if(text == null) return;
if(stringType.isNull((String) value)){
nullify();
return;
}
isBlank = false;
text.setVisibility(View.VISIBLE);
text.setText((String) value);
}
public void nullify(){
isBlank = true;
text.setVisibility(View.GONE);
}
public dataType getViewValue(){
if(text == null) return null;
if(text.getVisibility() == View.GONE) return new stringType(name, stringType.nullval);
return new stringType(name, text.getText().toString());
}
public void add_individual_view(LinearLayout parent, dataType 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) data.get());
tv.setTextSize(18);
parent.addView(tv);
}
float positive_mean = 0;
int count = 0;
TextView positive_text;
public void add_compiled_view(LinearLayout parent, dataType[] data) {
positive_mean = 0;
count = 0;
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(), new SentimentAnalysis.resultCallback() {
@Override
public void onFinish(float sentiment) {
positive_mean += sentiment;
count++;
positive_text.setText("Sentiment: " + (positive_mean / count));
}
});
}
}
}
public void add_history_view(LinearLayout parent, dataType[] data){
LineChart chart = new LineChart(parent.getContext());
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
layout.height = 350;
chart.setLayoutParams(layout);
chart.setBackgroundColor(0xff252025);
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,
SentimentAnalysis.analyse_sync( (String) data[i].get() )
)
);
}
LineDataSet dataSet = new LineDataSet(entries, "Sentiment");
dataSet.setColor(Color.BLUE);
dataSet.setValueTextColor(Color.BLACK);
dataSet.setDrawCircles(false);
dataSet.setDrawValues(false);
dataSet.setValueTextColor(Color.RED);
LineData lineData = new LineData(dataSet);
chart.setData(lineData);
chart.invalidate();
chart.getDescription().setEnabled(false);
chart.setTouchEnabled(false);
chart.setDragEnabled(false);
chart.setScaleEnabled(false);
chart.getXAxis().setTextColor(Color.WHITE);
chart.getAxisLeft().setTextColor(Color.WHITE);
chart.getAxisRight().setTextColor(Color.WHITE);
chart.getAxisLeft().setAxisMinimum(0.f);
chart.getAxisLeft().setAxisMaximum(1.f);
chart.getAxisRight().setAxisMinimum(0.f);
chart.getAxisRight().setAxisMaximum(1.f);
Legend legend = chart.getLegend();
legend.setTextColor(Color.WHITE);
chart.invalidate();
parent.addView(chart);
}
}
@@ -0,0 +1,126 @@
package com.ridgebotics.ridgescout.ui;
import static com.ridgebotics.ridgescout.utility.DataManager.evcode;
import static com.ridgebotics.ridgescout.utility.DataManager.event;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.databinding.FragmentTeamSelectorBinding;
import com.ridgebotics.ridgescout.types.frcTeam;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.DataManager;
import com.ridgebotics.ridgescout.utility.fileEditor;
import java.util.Arrays;
public class TeamSelectorFragment extends Fragment {
private FragmentTeamSelectorBinding binding;
private static boolean pits_mode;
public static void setPits_mode(boolean mode){
pits_mode = mode;
}
private static onTeamSelected onSelect = new onTeamSelected() {@Override public void onSelect(TeamSelectorFragment self, frcTeam team) {}};
public interface onTeamSelected {
void onSelect(TeamSelectorFragment self, frcTeam team);
}
public static void setOnSelect(onTeamSelected tmponSelect){
onSelect = tmponSelect;
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentTeamSelectorBinding.inflate(inflater, container, false);
// event = fileEditor.g
DataManager.reload_event();
if(evcode == null || evcode.equals("unset")){
AlertManager.error("You somehow have not loaded an event!");
return binding.getRoot();
}
load_teams();
return binding.getRoot();
}
public void load_teams(){
// binding.pitFileIndicator.setVisibility(View.GONE);
// binding.pitTeamName.setVisibility(View.GONE);
// binding.pitTeamDescription.setVisibility(View.GONE);
//
// clear_fields();
int[] teamNums = new int[event.teams.size()];
for(int i = 0 ; i < event.teams.size(); i++){
teamNums[i] = event.teams.get(i).teamNumber;
}
Arrays.sort(teamNums);
TableLayout table = new TableLayout(getContext());
table.setStretchAllColumns(true);
binding.teams.addView(table);
for(int i = 0; i < event.teams.size(); i++){
frcTeam team = null;
for(int a = 0 ; a < event.teams.size(); a++){
if(event.teams.get(a).teamNumber == teamNums[i]){
team = event.teams.get(a);
break;
}
}
TableRow tr = new TableRow(getContext());
TableLayout.LayoutParams rowParams = new TableLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT
);
rowParams.setMargins(20,20,20,20);
tr.setLayoutParams(rowParams);
tr.setPadding(20,20,20,20);
table.addView(tr);
if(!pits_mode || fileEditor.fileExist(evcode + "-" + team.teamNumber + ".pitscoutdata")){
tr.setBackgroundColor(0x3000FF00);
}else{
tr.setBackgroundColor(0x30FF0000);
}
TextView tv = new TextView(getContext());
tv.setText(String.valueOf(team.teamNumber));
tv.setTextSize(20);
tr.addView(tv);
tv = new TextView(getContext());
tv.setText(team.teamName);
tv.setTextSize(16);
tr.addView(tv);
frcTeam finalTeam = team;
tr.setOnClickListener(v -> {
onSelect.onSelect(this, finalTeam);
});
}
}
}
@@ -0,0 +1,24 @@
package com.ridgebotics.ridgescout.ui.data;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.databinding.FragmentDataCompareBinding;
import com.ridgebotics.ridgescout.databinding.FragmentDataReportBinding;
public class CompareFragment extends Fragment {
FragmentDataCompareBinding binding;
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentDataCompareBinding.inflate(inflater, container, false);
return binding.getRoot();
}
}
@@ -0,0 +1,79 @@
package com.ridgebotics.ridgescout.ui.data;
import static androidx.navigation.fragment.FragmentKt.findNavController;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.SettingsVersionStack.latestSettings;
import com.ridgebotics.ridgescout.databinding.FragmentDataBinding;
import com.ridgebotics.ridgescout.types.frcTeam;
import com.ridgebotics.ridgescout.ui.TeamSelectorFragment;
import com.ridgebotics.ridgescout.utility.fileEditor;
import com.ridgebotics.ridgescout.types.frcEvent;
public class DataFragment extends Fragment {
private FragmentDataBinding binding;
private boolean submenu = false;
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentDataBinding.inflate(inflater, container, false);
View root = binding.getRoot();
String evcode = latestSettings.settings.get_evcode();
binding.fieldsButton.setOnClickListener(v -> {
findNavController(this).navigate(R.id.action_navigation_data_to_navigation_data_fields_chooser);
});
if(evcode.equals("unset")){
binding.noEventError.setVisibility(View.VISIBLE);
binding.buttons.setVisibility(View.VISIBLE);
binding.teamsButton.setEnabled(false);
binding.compareButton.setEnabled(false);
binding.reportButton.setEnabled(false);
binding.fieldsButton.setVisibility(View.VISIBLE);
return root;
}
frcEvent event = frcEvent.decode(fileEditor.readFile(evcode + ".eventdata"));
binding.teamsButton.setOnClickListener(v -> {
TeamSelectorFragment.setPits_mode(false);
TeamSelectorFragment.setOnSelect(new TeamSelectorFragment.onTeamSelected() {
@Override
public void onSelect(TeamSelectorFragment self, frcTeam team) {
TeamsFragment.setTeam(team);
findNavController(self).navigate(R.id.action_navigation_team_selector_to_navigation_data_teams);
}
});
findNavController(this).navigate(R.id.action_navigation_data_to_navigation_team_selector);
});
binding.compareButton.setOnClickListener(v -> {
findNavController(this).navigate(R.id.action_navigation_data_to_navigation_data_compare);
});
binding.reportButton.setOnClickListener(v -> {
findNavController(this).navigate(R.id.action_navigation_data_to_navigation_data_report);
});
return root;
}
}
@@ -0,0 +1,249 @@
package com.ridgebotics.ridgescout.ui.data;
import static android.text.InputType.TYPE_CLASS_NUMBER;
import android.content.Context;
import android.view.View;
import android.widget.EditText;
import android.widget.TableLayout;
import android.widget.TextView;
import com.ridgebotics.ridgescout.types.input.dropdownType;
import com.ridgebotics.ridgescout.types.input.inputType;
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.utility.AlertManager;
public class FieldEditorHelper {
private enum parameterTypeEnum {
paramNumber,
paramString,
paramStringArray
}
public static class parameterType {
public String name;
public parameterTypeEnum id;
}
public static class paramNumber extends parameterType{
public int val;
public paramNumber(String name, int val){
this.name = name + " (Number)";
this.val = val;
this.id = parameterTypeEnum.paramNumber;
}
}
public static class paramString extends parameterType {
public String val;
public paramString(String name, String val){
this.name = name + " (String)";
this.val = val;
this.id = parameterTypeEnum.paramString;
}
}
public static class paramStringArray extends parameterType{
public String[] val;
public paramStringArray(String name, String[] val){
this.name = name + " (String array)";
this.val = val;
this.id = parameterTypeEnum.paramStringArray;
}
}
public static final parameterType[] defaultSliderParams = new parameterType[]{
new paramNumber("Min", 0),
new paramNumber("Max", 10),
new paramNumber("Default Value", 5)
};
public static final parameterType[] defaultDropdownParams = new parameterType[]{
new paramStringArray("Default Value", new String[]{"Zero","One","Two","Three"}),
new paramNumber("Default Option", 0),
};
public static final parameterType[] defaultTextParams = new parameterType[]{
new paramString("Default Value", "")
};
public static final parameterType[] defaultTallyParams = new parameterType[]{
new paramNumber("Default Value", 0)
};
private static parameterType[] getSliderParams(sliderType s){
return new parameterType[]{
new paramNumber("Min", s.min),
new paramNumber("Max", s.max),
new paramNumber("Default Value", (int) s.default_value)
};
}
private static parameterType[] getDropdownParams(dropdownType s){
return new parameterType[]{
new paramStringArray("Default Value",s.text_options),
new paramNumber("Default Option", (int) s.default_value),
};
}
private static parameterType[] getTextParams(textType s){
return new parameterType[]{
new paramString("Default Value", (String) s.default_value)
};
}
private static parameterType[] getTallyParams(tallyType s){
return new parameterType[]{
new paramNumber("Default Value", (int) s.default_value)
};
}
public static void setSliderParams(sliderType s, parameterType[] types){
s.min = ((paramNumber) types[0]).val;
s.max = ((paramNumber) types[1]).val;
s.default_value = ((paramNumber) types[2]).val;
}
public static void setDropdownParams(dropdownType s, parameterType[] types){
s.text_options = ((paramStringArray) types[0]).val;
s.default_value = ((paramNumber) types[1]).val;
}
public static void setTextParams(textType s, parameterType[] types){
s.default_value = ((paramString) types[0]).val;
}
public static void setTallyParams(tallyType s, parameterType[] types){
s.default_value = ((paramNumber) types[0]).val;
}
private static void setInputParameter(inputType t, parameterType[] types){
switch (t.getInputType()){
case TALLY:
setTallyParams((tallyType) t, types);
break;
case SLIDER:
setSliderParams((sliderType) t, types);
break;
case DROPDOWN:
setDropdownParams((dropdownType) t, types);
break;
case NOTES_INPUT:
setTextParams((textType) t, types);
break;
}
}
private static parameterType[] getParamsFromInputType(inputType t){
switch (t.getInputType()){
case TALLY:
return getTallyParams((tallyType) t);
case SLIDER:
return getSliderParams((sliderType) t);
case DROPDOWN:
return getDropdownParams((dropdownType) t);
case NOTES_INPUT:
return getTextParams((textType) t);
}
return new parameterType[]{};
}
private static View createNumberEdit(Context c, int value){
EditText text = new EditText(c);
text.setInputType(TYPE_CLASS_NUMBER);
text.setText(String.valueOf(value));
return text;
}
private static View createStringEdit(Context c, String value){
EditText text = new EditText(c);
text.setText(value);
return text;
}
private static View createStringArrayEdit(Context c, String[] value){
EditText text = new EditText(c);
text.setText(String.join("\n", value));
return text;
}
private static View createEdit(Context c, parameterType t){
switch (t.id){
case paramNumber:
return createNumberEdit(c, ((paramNumber) t).val);
case paramString:
return createStringEdit(c, ((paramString) t).val);
case paramStringArray:
return createStringArrayEdit(c, ((paramStringArray) t).val);
}
return null;
}
private static boolean readEdit(View v, parameterType t){
try{
String val;
switch (t.id) {
case paramNumber:
val = ((EditText) v).getText().toString();
if(val.isEmpty() || val.isBlank()) return false;
((paramNumber) t).val = Integer.parseInt(val);
break;
case paramString:
val = ((EditText) v).getText().toString();
//if(val.isEmpty() || val.isBlank()) return false;
((paramString) t).val = val;
break;
case paramStringArray:
val = ((EditText) v).getText().toString();
if(val.isEmpty() || val.isBlank()) return false;
((paramStringArray) t).val = val.split("\n");
break;
}
} catch (Exception e) {
AlertManager.error(e);
return false;
}
return true;
}
private parameterType[] types;
private View[] views;
private inputType t;
public FieldEditorHelper(Context c, inputType t, TableLayout parentView, parameterType[] tmptypes){
this.types = tmptypes;
this.t = t;
views = new View[types.length];
for(int i = 0; i < types.length; i++){
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]);
}
}
public FieldEditorHelper(Context c, inputType t, TableLayout parentView){
this(c,t,parentView,getParamsFromInputType(t));
}
public boolean save(){
for(int i = 0; i < types.length; i++){
if(!readEdit(views[i], types[i]))
return false;
}
setInputParameter(t, types);
return true;
}
}
@@ -0,0 +1,38 @@
package com.ridgebotics.ridgescout.ui.data;
import static androidx.navigation.fragment.FragmentKt.findNavController;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.databinding.FragmentDataFieldsChooserBinding;
import com.ridgebotics.ridgescout.scoutingData.fields;
public class FieldsChooserFragment extends Fragment {
FragmentDataFieldsChooserBinding binding;
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentDataFieldsChooserBinding.inflate(inflater, container, false);
binding.matchScoutingButton.setOnClickListener(v -> {
FieldsFragment.set_filename(fields.matchFieldsFilename);
findNavController(this).navigate(R.id.action_navigation_data_fields_chooser_to_navigation_data_fields);
});
binding.pitScoutingButton.setOnClickListener(v -> {
FieldsFragment.set_filename(fields.pitsFieldsFilename);
findNavController(this).navigate(R.id.action_navigation_data_fields_chooser_to_navigation_data_fields);
});
return binding.getRoot();
}
}
@@ -0,0 +1,522 @@
package com.ridgebotics.ridgescout.ui.data;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.text.InputType;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation;
import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.databinding.FragmentDataFieldsBinding;
import com.ridgebotics.ridgescout.scoutingData.fields;
import com.ridgebotics.ridgescout.types.input.dropdownType;
import com.ridgebotics.ridgescout.types.input.inputType;
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.utility.AlertManager;
import com.skydoves.powerspinner.IconSpinnerAdapter;
import com.skydoves.powerspinner.IconSpinnerItem;
import com.skydoves.powerspinner.PowerSpinnerView;
import com.skydoves.powerspinner.SpinnerGravity;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class FieldsFragment extends Fragment {
FragmentDataFieldsBinding binding;
private static String filename;
public static void set_filename(String tmpfilename){
filename = tmpfilename;
}
@SuppressLint("ClickableViewAccessibility")
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentDataFieldsBinding.inflate(inflater, container, false);
binding.revertVersionButton.setVisibility(View.VISIBLE);
binding.valueEditScrollview.setOnTouchListener((v, event) -> true);
binding.saveButton.setVisibility(View.GONE);
binding.cancelEditButton.setVisibility(View.GONE);
binding.editButton.setVisibility(View.GONE);
binding.upButton.setVisibility(View.GONE);
binding.addButton.setVisibility(View.GONE);
binding.downButton.setVisibility(View.GONE);
binding.deleteButton.setVisibility(View.GONE);
binding.valueEditContainer.setVisibility(View.GONE);
load_field_menu();
return binding.getRoot();
}
private static final int background_color = 0x5000ff00;
private static final int unfocused_background_color = 0x2000ff00;
inputType[][] values;
private void load_field_menu() {
values = fields.load(filename);
binding.fieldsArea.bringToFront();
binding.fieldsArea.setStretchAllColumns(true);
binding.fieldsArea.removeAllViews();
binding.fieldsArea.setReorderingEnabled(false);
if(values == null) return;
for(int i = 0; i < values.length; i++){
TableRow tr = new TableRow(getContext());
TableLayout.LayoutParams rowParams = new TableLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT
);
rowParams.setMargins(20,20,20,20);
tr.setLayoutParams(rowParams);
tr.setPadding(20,20,20,20);
TextView tv = new TextView(getContext());
tv.setText("v" + i);
tv.setTextSize(20);
tr.addView(tv);
tv = new TextView(getContext());
tv.setText(values[i].length + " Fields");
tv.setTextSize(16);
tr.addView(tv);
binding.fieldsArea.addView(tr);
if(i == values.length-1) {
tr.setBackgroundColor(background_color);
int fi = i;
tr.setOnClickListener(v -> {
display_fields(values[fi]);
});
}else{
tr.setBackgroundColor(unfocused_background_color);
}
}
}
private void display_fields(inputType[] version_values) {
binding.fieldsArea.removeAllViews();
binding.fieldsArea.setReorderingEnabled(false);
binding.revertVersionButton.setVisibility(View.GONE);
binding.saveButton.setVisibility(View.GONE);
binding.editButton.setVisibility(View.GONE);
binding.upButton.setVisibility(View.VISIBLE);
binding.addButton.setVisibility(View.VISIBLE);
binding.downButton.setVisibility(View.VISIBLE);
binding.valueEditContainer.setVisibility(View.GONE);
for(int i = 0; i < version_values.length; i++){
addRow(version_values[i]);
}
selindex = -1;
binding.addButton.setOnClickListener(this::addField);
binding.saveButton.setOnClickListener(this::buttonfunc);
}
private void addRow(inputType field){
TableRow tr = getTableRow(field);
binding.fieldsArea.addView(tr);
tr.setOnClickListener(v -> {
binding.editButton.setVisibility(View.VISIBLE);
trOnClick(values[values.length-1], tr);
});
binding.upButton.setOnClickListener(v -> {
if(selindex != 0) {
binding.saveButton.setVisibility(View.VISIBLE);
binding.fieldsArea.updateRowOrder(selindex, selindex - 1);
selindex -= 1;
}
});
binding.downButton.setOnClickListener(v -> {
if(selindex != values[values.length-1].length-1) {
binding.saveButton.setVisibility(View.VISIBLE);
binding.fieldsArea.updateRowOrder(selindex, selindex + 1);
selindex += 1;
}
});
}
private void buttonfunc(View v){
AlertDialog.Builder alert = new AlertDialog.Builder(getContext());
alert.setTitle("Warning!");
alert.setMessage("Changing or removing some values will result in lost data!\nBut this will create a new field version, and you can revert at any time.");
alert.setPositiveButton("OK", null);
alert.setNegativeButton("Cancel", null);
alert.setCancelable(true);
alert.setOnDismissListener(b -> {
inputType[][] currentValues = fields.load(filename);
assert currentValues != null;
inputType[][] newValues = new inputType[currentValues.length+1][];
System.arraycopy(currentValues, 0, newValues, 0, currentValues.length);
newValues[newValues.length-1] = new inputType[values[values.length-1].length];
for(int i = 0; i < values[values.length-1].length; i++){
newValues[newValues.length-1][i] = values[values.length-1][binding.fieldsArea.getReorderedIndexes().get(i)];
}
// newValues[newValues.length-1] = values[values.length-1];
boolean saved = fields.save(filename, newValues);
AlertManager.alert("Saved", String.valueOf(saved));
Navigation.findNavController((Activity) getContext(), R.id.nav_host_fragment_activity_main).navigate(R.id.action_navigation_data_fields_to_navigation_data_fields_chooser);
});
alert.create().show();
}
private int selindex = -1;
private void trOnClick(inputType[] version_values, TableRow tr){
selindex = -1;
for(int i = 0; i < binding.fieldsArea.getChildCount(); i++){
View v = binding.fieldsArea.getChildAt(i);
if(v.equals(tr)) {
tr.setBackgroundColor(background_color);
selindex = i;
} else
binding.fieldsArea.getChildAt(i).setBackgroundColor(unfocused_background_color);
}
onFieldSelect(version_values[binding.fieldsArea.getReorderedIndexes().get(selindex)]);
}
private void onFieldSelect(inputType field){
//System.out.println(field.name);
binding.editButton.setOnClickListener(v -> {
binding.editButton.setVisibility(View.GONE);
binding.addButton.setVisibility(View.GONE);
binding.upButton.setVisibility(View.GONE);
binding.downButton.setVisibility(View.GONE);
binding.ValueEditTable.removeAllViews();
binding.valueEditContainer.setVisibility(View.VISIBLE);
TextView tv = new TextView(getContext());
tv.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
tv.setText(field.name);
tv.setPadding(8,8,8,8);
tv.setTextSize(24);
binding.ValueEditTable.addView(tv);
final FieldEditorHelper fe = new FieldEditorHelper(
getContext(),
field,
binding.ValueEditTable
);
binding.saveButton.setVisibility(View.VISIBLE);
binding.saveButton.setOnClickListener(a -> {
System.out.println(fe.save());
defaultVisibility();
});
binding.cancelEditButton.setVisibility(View.VISIBLE);
binding.cancelEditButton.setOnClickListener(a -> {
defaultVisibility();
});
binding.deleteButton.setVisibility(View.VISIBLE);
binding.deleteButton.setOnClickListener(a -> {
deleteField(field);
});
});
}
private void deleteField(inputType field){
AlertDialog.Builder alert = new AlertDialog.Builder(getContext());
alert.setTitle("Warning!");
alert.setMessage("Removing a value will result in data being deleted in subsequent field versions!");
alert.setNegativeButton("Cancel", null);
alert.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
defaultVisibility();
int oldindex = -1;
for(int i = 0; i < values[values.length - 1].length; i++){
if(values[values.length - 1][i].equals(field)){
oldindex = i;
break;
}
}
if(oldindex != -1) {
System.out.println(Arrays.toString(values[values.length - 1]));
binding.fieldsArea.removeViewAt(selindex);
binding.fieldsArea.removeElement(oldindex);
values[values.length - 1] = removeElement(values[values.length - 1], oldindex);
selindex = -1;
AlertManager.toast("Removed!");
binding.editButton.setVisibility(View.GONE);
System.out.println(Arrays.toString(values[values.length - 1]));
//System.out.println(values[values.length-1].length);
}
}
});
alert.setCancelable(true);
alert.create().show();
}
public inputType[] removeElement(inputType[] src, int i) {
inputType[] newArray = new inputType[src.length - 1];
if (i > 0){
System.arraycopy(src, 0, newArray, 0, i);
}
if (newArray.length > i){
System.arraycopy(src, i + 1, newArray, i, newArray.length - i);
}
return newArray;
}
private void defaultVisibility() {
binding.editButton.setVisibility(View.VISIBLE);
binding.addButton.setVisibility(View.VISIBLE);
binding.upButton.setVisibility(View.VISIBLE);
binding.downButton.setVisibility(View.VISIBLE);
binding.ValueEditTable.removeAllViews();
binding.valueEditContainer.setVisibility(View.GONE);
binding.cancelEditButton.setVisibility(View.GONE);
binding.deleteButton.setVisibility(View.GONE);
binding.saveButton.setOnClickListener(this::buttonfunc);
}
private void addField(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle("Title");
final EditText input = new EditText(getContext());
input.setInputType(InputType.TYPE_CLASS_TEXT);
builder.setView(input);
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String title = input.getText().toString();
if(title.isEmpty() || title.isBlank()) {
AlertManager.error("Title cannot be blank!");
return;
}
addField_Part_2(title);
}
});
builder.show();
}
private void addField_Part_2(String title) {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle("Title");
final PowerSpinnerView dropdown = new PowerSpinnerView(getContext());
List<IconSpinnerItem> iconSpinnerItems = new ArrayList<>();
iconSpinnerItems.add(new IconSpinnerItem("Slider"));
iconSpinnerItems.add(new IconSpinnerItem("Text"));
iconSpinnerItems.add(new IconSpinnerItem("Dropdown"));
iconSpinnerItems.add(new IconSpinnerItem("Tally"));
IconSpinnerAdapter iconSpinnerAdapter = new IconSpinnerAdapter(dropdown);
dropdown.setGravity(Gravity.CENTER);
dropdown.setSpinnerAdapter(iconSpinnerAdapter);
dropdown.setItems(iconSpinnerItems);
dropdown.selectItemByIndex(0);
dropdown.setPadding(10,20,10,20);
dropdown.setBackgroundColor(0xf0000000);
dropdown.setTextColor(0xff00ff00);
dropdown.setTextSize(14.5f);
dropdown.setArrowGravity(SpinnerGravity.END);
dropdown.setArrowPadding(8);
// dropdown.setSpinnerItemHeight(46);
dropdown.setSpinnerPopupElevation(14);
builder.setView(dropdown);
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
addField_Part_3(title, dropdown.getSelectedIndex());
}
});
builder.show();
}
private void addField_Part_3(String title, int typeIndex) {
switch (typeIndex){
case 0:
sliderType slider = new sliderType();
slider.name = title;
FieldEditorHelper.setSliderParams(slider, FieldEditorHelper.defaultSliderParams);
addField_Part_4(slider);
break;
case 1:
textType text = new textType();
text.name = title;
FieldEditorHelper.setTextParams(text, FieldEditorHelper.defaultTextParams);
addField_Part_4(text);
break;
case 2:
dropdownType dropdown = new dropdownType();
dropdown.name = title;
FieldEditorHelper.setDropdownParams(dropdown, FieldEditorHelper.defaultDropdownParams);
addField_Part_4(dropdown);
break;
case 3:
tallyType tally = new tallyType();
tally.name = title;
FieldEditorHelper.setTallyParams(tally, FieldEditorHelper.defaultTallyParams);
addField_Part_4(tally);
break;
}
}
private void addField_Part_4(inputType field) {
binding.editButton.setVisibility(View.GONE);
binding.addButton.setVisibility(View.GONE);
binding.upButton.setVisibility(View.GONE);
binding.downButton.setVisibility(View.GONE);
binding.ValueEditTable.removeAllViews();
binding.valueEditContainer.setVisibility(View.VISIBLE);
TextView tv = new TextView(getContext());
tv.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
tv.setText(field.name);
tv.setPadding(8,8,8,8);
tv.setTextSize(24);
binding.ValueEditTable.addView(tv);
final FieldEditorHelper fe = new FieldEditorHelper(
getContext(),
field,
binding.ValueEditTable
);
binding.saveButton.setVisibility(View.VISIBLE);
binding.saveButton.setOnClickListener(a -> {
inputType[] newValues = new inputType[values[values.length-1].length+1];
System.arraycopy(values[values.length-1], 0, newValues, 0, values[values.length-1].length);
newValues[newValues.length-1] = field;
values[values.length-1] = newValues;
AlertManager.alert("Test", String.valueOf(binding.fieldsArea.getReorderedIndexes()));
//TableRow tr = getTableRow(field);
//binding.fieldsArea.addView(tr);
addRow(field);
System.out.println(fe.save());
defaultVisibility();
});
binding.cancelEditButton.setVisibility(View.VISIBLE);
binding.cancelEditButton.setOnClickListener(a -> {
defaultVisibility();
});
}
private @NonNull TableRow getTableRow(inputType field) {
TableRow tr = new TableRow(getContext());
TableLayout.LayoutParams rowParams = new TableLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT
);
rowParams.setMargins(20,20,20,20);
tr.setLayoutParams(rowParams);
tr.setPadding(20,20,20,20);
tr.setBackgroundColor(unfocused_background_color);
TextView tv = new TextView(getContext());
tv.setText(field.get_type_name());
tv.setTextSize(12);
tr.addView(tv);
tv = new TextView(getContext());
tv.setText(field.name);
tv.setTextSize(20);
tr.addView(tv);
return tr;
}
}
@@ -0,0 +1,24 @@
package com.ridgebotics.ridgescout.ui.data;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.databinding.FragmentDataReportBinding;
public class ReportFragment extends Fragment {
FragmentDataReportBinding binding;
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentDataReportBinding.inflate(inflater, container, false);
return binding.getRoot();
}
}
@@ -0,0 +1,384 @@
package com.ridgebotics.ridgescout.ui.data;
import static com.ridgebotics.ridgescout.utility.DataManager.evcode;
import static com.ridgebotics.ridgescout.utility.DataManager.match_latest_values;
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_latest_values;
import static com.ridgebotics.ridgescout.utility.DataManager.pit_transferValues;
import static com.ridgebotics.ridgescout.utility.DataManager.pit_values;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TableLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.SettingsVersionStack.latestSettings;
import com.ridgebotics.ridgescout.databinding.FragmentDataTeamsBinding;
import com.ridgebotics.ridgescout.scoutingData.ScoutingDataWriter;
import com.ridgebotics.ridgescout.types.data.dataType;
import com.ridgebotics.ridgescout.types.frcTeam;
import com.ridgebotics.ridgescout.utility.DataManager;
import com.ridgebotics.ridgescout.utility.fileEditor;
import com.google.android.material.divider.MaterialDivider;
import com.skydoves.powerspinner.IconSpinnerAdapter;
import com.skydoves.powerspinner.IconSpinnerItem;
import com.skydoves.powerspinner.OnSpinnerItemSelectedListener;
import com.skydoves.powerspinner.PowerSpinnerView;
import com.skydoves.powerspinner.SpinnerGravity;
import java.util.ArrayList;
import java.util.List;
public class TeamsFragment extends Fragment {
FragmentDataTeamsBinding binding;
private static frcTeam team;
public static void setTeam(frcTeam tmpteam){
team = tmpteam;
}
private static final int background_color = 0x5000ff00;
private static final int unsaved_background_color = 0x2000ff00;
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentDataTeamsBinding.inflate(inflater, container, false);
binding.teamsArea.removeAllViews();
DataManager.reload_match_fields();
DataManager.reload_pit_fields();
TableLayout table = new TableLayout(getContext());
table.setStretchAllColumns(true);
binding.teamsArea.addView(table);
loadTeam(latestSettings.settings.get_data_view_mode());
return binding.getRoot();
}
public void loadTeam(int mode) {
binding.teamsArea.removeAllViews();
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);
PowerSpinnerView dropdown = new PowerSpinnerView(getContext());
List<IconSpinnerItem> iconSpinnerItems = new ArrayList<>();
iconSpinnerItems.add(new IconSpinnerItem("Individual"));
iconSpinnerItems.add(new IconSpinnerItem("Compiled"));
iconSpinnerItems.add(new IconSpinnerItem("History"));
IconSpinnerAdapter iconSpinnerAdapter = new IconSpinnerAdapter(dropdown);
dropdown.setSpinnerAdapter(iconSpinnerAdapter);
dropdown.setItems(iconSpinnerItems);
dropdown.selectItemByIndex(0);
dropdown.setPadding(10,20,10,20);
dropdown.setBackgroundColor(0xf0000000);
dropdown.setTextColor(0xff00ff00);
dropdown.setTextSize(15);
dropdown.setArrowGravity(SpinnerGravity.END);
dropdown.setArrowPadding(8);
// dropdown.setSpinnerItemHeight(46);
dropdown.setSpinnerPopupElevation(14);
dropdown.selectItemByIndex(mode);
dropdown.setOnSpinnerItemSelectedListener(new OnSpinnerItemSelectedListener<IconSpinnerItem>() {
@Override
public void onItemSelected(int oldIndex, @Nullable IconSpinnerItem oldItem, int newIndex,
IconSpinnerItem newItem) {
latestSettings.settings.set_data_view_mode(newIndex);
loadTeam(newIndex);
}
});
ll.addView(dropdown);
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(String.valueOf(team.teamNumber));
tv.setTextSize(28);
ll.addView(tv);
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.teamName);
tv.setTextSize(28);
ll.addView(tv);
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);
add_pit_data(ll, team);
add_match_data(ll, team, mode);
}
public void add_pit_data(LinearLayout ll, frcTeam team){
final String filename = evcode+"-"+team.teamNumber+".pitscoutdata";
ll.addView(new MaterialDivider(getContext()));
TextView tv = new TextView(getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setPadding(0,10,0,10);
tv.setText("----- Pit data -----");
tv.setTextSize(30);
ll.addView(tv);
ll.addView(new MaterialDivider(getContext()));
if(!fileEditor.fileExist(filename)){
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);
ll.addView(tv);
return;
}
ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(filename, pit_values, pit_transferValues);
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);
ll.addView(tv);
for (int a = 0; a < psda.data.array.length; a++) {
tv = new TextView(getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText(psda.data.array[a].getName());
tv.setTextSize(25);
if(psda.data.array[a].isNull()){
tv.setBackgroundColor(0xffff0000);
tv.setTextColor(0xff000000);
}
ll.addView(tv);
pit_latest_values[a].add_individual_view(ll, psda.data.array[a]);
}
}
public void add_match_data(LinearLayout ll, frcTeam team, int mode){
String[] files = fileEditor.getMatchesByTeamNum(evcode, team.teamNumber);
ll.addView(new MaterialDivider(getContext()));
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("----- Match data -----");
tv.setPadding(0,10,0,10);
tv.setTextSize(30);
ll.addView(tv);
ll.addView(new MaterialDivider(getContext()));
if(files.length == 0){
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);
ll.addView(tv);
return;
}
switch (mode){
case 0:
add_individual_views(ll,files);
break;
case 1:
add_compiled_views(ll,files);
break;
case 2:
add_history_views(ll,files);
break;
}
}
public void add_individual_views(LinearLayout ll, String[] files) {
for (int i = 0; i < files.length; i++) {
String[] split = files[i].split("-");
int match_num = Integer.parseInt(split[1]);
ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(files[i], match_values, match_transferValues);
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);
ll.addView(tv);
for (int a = 0; a < psda.data.array.length; a++) {
tv = new TextView(getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText(psda.data.array[a].getName());
tv.setTextSize(25);
if (psda.data.array[a].isNull()) {
tv.setBackgroundColor(0xffff0000);
tv.setTextColor(0xff000000);
}
ll.addView(tv);
match_latest_values[a].add_individual_view(ll, psda.data.array[a]);
}
}
}
public void add_compiled_views(LinearLayout ll, String[] files){
dataType[][] data = new dataType[match_latest_values.length][files.length];
for (int i = 0; i < files.length; i++) {
ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(files[i], match_values, match_transferValues);
for (int a = 0; a < data.length; a++) {
data[a][i] = psda.data.array[a];
}
}
for(int i = 0; i < match_latest_values.length; i++){
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);
ll.addView(tv);
match_latest_values[i].add_compiled_view(ll, data[i]);
}
}
public void add_history_views(LinearLayout ll, String[] files){
dataType[][] data = new dataType[match_latest_values.length][files.length];
for (int i = 0; i < files.length; i++) {
ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(files[i], match_values, match_transferValues);
for (int a = 0; a < data.length; a++) {
data[a][i] = psda.data.array[a];
}
}
for(int i = 0; i < match_latest_values.length; i++){
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);
ll.addView(tv);
match_latest_values[i].add_history_view(ll, data[i]);
}
}
}
@@ -0,0 +1,375 @@
package com.ridgebotics.ridgescout.ui.scouting;
import static com.ridgebotics.ridgescout.utility.DataManager.evcode;
import static com.ridgebotics.ridgescout.utility.DataManager.event;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.SettingsVersionStack.latestSettings;
import com.ridgebotics.ridgescout.databinding.FragmentScoutingMatchBinding;
import com.ridgebotics.ridgescout.scoutingData.ScoutingDataWriter;
import com.ridgebotics.ridgescout.types.data.dataType;
import com.ridgebotics.ridgescout.types.frcMatch;
import com.ridgebotics.ridgescout.types.frcTeam;
import com.ridgebotics.ridgescout.types.input.inputType;
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 java.util.ArrayList;
import java.util.function.Function;
public class MatchScoutingFragment extends Fragment {
private FragmentScoutingMatchBinding binding;
@SuppressLint("ClickableViewAccessibility")
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentScoutingMatchBinding.inflate(inflater, container, false);
DataManager.reload_match_fields();
alliance_position = latestSettings.settings.get_alliance_pos();
username = latestSettings.settings.get_username();
binding.username.setText(username);
binding.alliancePosText.setText(alliance_position);
binding.teamDescription.setVisibility(View.GONE);
binding.teamName.setVisibility(View.GONE);
clear_fields();
binding.teamDescription.setVisibility(View.VISIBLE);
binding.teamName.setVisibility(View.VISIBLE);
if(DataManager.match_values == null || DataManager.match_values.length == 0){
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();
}
cur_match_num = latestSettings.settings.get_match_num();
update_match_num();
binding.nextButton.setOnClickListener(v -> {
if(edited) save();
latestSettings.settings.set_match_num(cur_match_num+1);
cur_match_num += 1;
update_match_num();
update_scouting_data();
});
boolean fileIndicatorTapped = false;
binding.fileIndicator.setOnClickListener(v -> {
// if(e.getAction() != MotionEvent.ACTION_MOVE) return true;
// System.out.println(e.getAxisValue(0));
if(edited) save();
alliance_position = incrementMatchPos(alliance_position);
latestSettings.settings.set_alliance_pos(alliance_position);
binding.alliancePosText.setText(alliance_position);
update_match_num();
update_scouting_data();
// return true;
});
binding.backButton.setOnClickListener(v -> {
if(edited) save();
latestSettings.settings.set_match_num(cur_match_num-1);
cur_match_num -= 1;
update_match_num();
update_scouting_data();
});
// binding.middleButton.setOnClickListener(v -> {
// if(edited) save();
// });
create_fields();
update_scouting_data();
return binding.getRoot();
}
private static String incrementMatchPos(String input){
switch(input){ // There's probably a better solution than this.
case "red-1":
return "red-2";
case "red-2":
return "red-3";
case "red-3":
return "blue-1";
case "blue-1":
return "blue-2";
case "blue-2":
return "blue-3";
case "blue-3":
return "red-1";
}
return "red-1";
}
private static final int unsaved_color = 0x60ff0000;
private static final int saved_color = 0x6000ff00;
String alliance_position;
int cur_match_num;
String username;
String filename;
boolean edited = false;
TextView[] titles;
AutoSaveManager asm = new AutoSaveManager(this::save);
ArrayList<dataType> dataTypes;
public void save(){
System.out.println("Saved!");
edited = false;
set_indicator_color(saved_color);
AlertManager.toast("Saved " + filename);
save_fields();
}
public void set_indicator_color(int color){
binding.fileIndicator.setBackgroundColor(color);
}
public void update_asm(){
// v.getBackground().setColorFilter(Color.parseColor("#00ff00"), PorterDuff.Mode.DARKEN);
edited = true;
set_indicator_color(unsaved_color);
asm.update();
}
public void clear_fields(){
int childCount = binding.MatchScoutArea.getChildCount();
View[] views = new View[childCount];
for(int i = 0; i < childCount; i++){
views[i] = binding.MatchScoutArea.getChildAt(i);
}
for(int i = 0; i < childCount; i++){
if(!views[i].isShown()) continue;
binding.MatchScoutArea.removeView(views[i]);
}
}
private int default_text_color = 0;
private void create_fields(){
if(asm.isRunning){
asm.stop();
}
titles = new TextView[DataManager.match_latest_values.length];
for(int i = 0 ; i < DataManager.match_latest_values.length; i++) {
final TextView tv = new TextView(getContext());
tv.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
tv.setText(DataManager.match_latest_values[i].name);
tv.setPadding(8,8,8,8);
tv.setTextSize(24);
titles[i] = tv;
default_text_color = tv.getCurrentTextColor();
final View v = DataManager.match_latest_values[i].createView(getContext(), new Function<dataType, Integer>() {
@Override
public Integer apply(dataType dataType) {
// edited = true;
if(asm.isRunning)
update_asm();
return 0;
}
});
binding.MatchScoutArea.addView(tv);
int fi = i;
tv.setOnClickListener(p -> {
// boolean blank = !latest_values[fi].getViewValue().isNull();
// System.out.println(blank);
if(asm.isRunning)
update_asm();
if(!DataManager.match_latest_values[fi].isBlank){
tv.setBackgroundColor(0xffff0000);
tv.setTextColor(0xff000000);
DataManager.match_latest_values[fi].nullify();
}else{
tv.setBackgroundColor(0x00000000);
tv.setTextColor(default_text_color);
DataManager.match_latest_values[fi].setViewValue(DataManager.match_latest_values[fi].default_value);
}
});
binding.MatchScoutArea.addView(v);
}
}
private void update_match_num(){
// cur_match_num = latestSettings.settings.get_match_num();
edited = false;
binding.matchnum.setText(String.valueOf(cur_match_num+1));
if(cur_match_num <= 0){
binding.backButton.setVisibility(View.GONE);
}else{
binding.backButton.setVisibility(View.VISIBLE);
}
if(cur_match_num >= event.matches.size()-1){
binding.nextButton.setVisibility(View.GONE);
}else{
binding.nextButton.setVisibility(View.VISIBLE);
}
}
private frcTeam get_team(frcMatch match){
// Get team number
String[] split = alliance_position.split("-");
Integer team_num = null;
switch (split[0]){
case "red":
team_num = match.redAlliance[Integer.parseInt(split[1])-1];
break;
case "blue":
team_num = match.blueAlliance[Integer.parseInt(split[1])-1];
break;
}
binding.barTeamNum.setText(String.valueOf(team_num));
frcTeam team = null;
for(int i=0; i < event.teams.size(); i++){
frcTeam tmpteam = event.teams.get(i);
if(tmpteam.teamNumber == team_num){
team = tmpteam;
break;
}
}
filename = evcode + "-" + (cur_match_num+1) + "-" + alliance_position + "-" + team_num + ".matchscoutdata";
return team;
}
public void update_scouting_data(){
frcMatch match = event.matches.get(cur_match_num);
frcTeam team = get_team(match);
binding.teamName.setText(team.teamName);
binding.teamDescription.setText(team.getDescription());
boolean new_file = !fileEditor.fileExist(filename);
if(asm.isRunning){
asm.stop();
}
if(new_file){
default_fields();
set_indicator_color(unsaved_color);
}else{
get_fields();
set_indicator_color(saved_color);
}
asm.start();
}
public void default_fields(){
for(int i = 0; i < DataManager.match_latest_values.length; i++){
inputType input = DataManager.match_latest_values[i];
input.setViewValue(input.default_value);
titles[i].setBackgroundColor(0x00000000);
titles[i].setTextColor(default_text_color);
}
}
public void get_fields(){
ScoutingDataWriter.ParsedScoutingDataResult psdr = ScoutingDataWriter.load(filename, DataManager.match_values, DataManager.match_transferValues);
dataType[] types = psdr.data.array;
for(int i = 0; i < DataManager.match_latest_values.length; i++){
// types[i] = latest_values[i].getViewValue();
try {
DataManager.match_latest_values[i].setViewValue(types[i].get());
} catch (Exception e){
AlertManager.error(e);
DataManager.match_latest_values[i].setViewValue(DataManager.match_latest_values[i].default_value);
}
if(DataManager.match_latest_values[i].isBlank){
titles[i].setBackgroundColor(0xffff0000);
titles[i].setTextColor(0xff000000);
}else{
titles[i].setBackgroundColor(0x00000000);
titles[i].setTextColor(default_text_color);
}
}
}
public void save_fields(){
dataType[] types = new dataType[DataManager.match_latest_values.length];
for(int i = 0; i < DataManager.match_latest_values.length; i++){
types[i] = DataManager.match_latest_values[i].getViewValue();
}
if(ScoutingDataWriter.save(DataManager.match_values.length-1, username, filename, types))
System.out.println("Saved!");
else
System.out.println("Error saving");
}
}
@@ -0,0 +1,257 @@
package com.ridgebotics.ridgescout.ui.scouting;
import static com.ridgebotics.ridgescout.utility.DataManager.evcode;
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.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.SettingsVersionStack.latestSettings;
import com.ridgebotics.ridgescout.databinding.FragmentScoutingPitBinding;
import com.ridgebotics.ridgescout.scoutingData.ScoutingDataWriter;
import com.ridgebotics.ridgescout.types.data.dataType;
import com.ridgebotics.ridgescout.types.frcTeam;
import com.ridgebotics.ridgescout.types.input.inputType;
import com.ridgebotics.ridgescout.utility.AutoSaveManager;
import com.ridgebotics.ridgescout.utility.DataManager;
import com.ridgebotics.ridgescout.utility.fileEditor;
import java.util.ArrayList;
import java.util.function.Function;
public class PitScoutingFragment extends Fragment {
FragmentScoutingPitBinding binding;
private static frcTeam team;
public static void setTeam(frcTeam tmpteam){
team = tmpteam;
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentScoutingPitBinding.inflate(inflater, container, false);
username = latestSettings.settings.get_username();
DataManager.reload_pit_fields();
loadTeam();
return binding.getRoot();
}
private static final int unsaved_color = 0x60ff0000;
private static final int saved_color = 0x6000ff00;
boolean edited = false;
String filename;
String username;
TextView[] titles;
AutoSaveManager asm = new AutoSaveManager(this::save);
ArrayList<dataType> dataTypes;
public void save(){
edited = false;
set_indicator_color(saved_color);
dataType[] types = new dataType[pit_latest_values.length];
for(int i = 0; i < pit_latest_values.length; i++){
types[i] = pit_latest_values[i].getViewValue();
}
if(ScoutingDataWriter.save(pit_values.length-1, username, filename, types))
System.out.println("Saved!");
else
System.out.println("Error saving");
}
public void set_indicator_color(int color){
binding.pitFileIndicator.setBackgroundColor(color);
}
public void update_asm(){
// v.getBackground().setColorFilter(Color.parseColor("#00ff00"), PorterDuff.Mode.DARKEN);
edited = true;
set_indicator_color(unsaved_color);
asm.update();
}
// public void init(frcEvent event){
//
// evcode = event.eventCode;
// this.event = event;
// username = latestSettings.settings.get_username();
//
//// binding.eventcode.setText(evcode);
//
// binding.pitBackButton.setOnClickListener(view -> {
// if(edited) save();
// binding.pitTeamName.setVisibility(View.GONE);
// binding.pitTeamDescription.setVisibility(View.GONE);
// clear_fields();
// load_teams();
// });
//
// values = fields.load(fields.pitsFieldsFilename);
//
// if(values == null || values.length == 0){
// TextView tv = new TextView(getContext());
// tv.setText("Failed to load fields.\nTry to either download or create pit scouting fields.");
// tv.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
// binding.pitScoutArea.addView(tv);
// return;
// }
//
// latest_values = values[values.length-1];
// transferValues = transferType.get_transfer_values(values);
// }
//
// public void clear_fields(){
// int childCount = binding.pitScoutArea.getChildCount();
// View[] views = new View[childCount];
//
// for(int i = 0; i < childCount; i++){
// views[i] = binding.pitScoutArea.getChildAt(i);
// }
//
// for(int i = 0; i < childCount; i++){
// if(!views[i].isShown()) continue;
// binding.pitScoutArea.removeView(views[i]);
// }
// }
public void loadTeam(){
// clear_fields();
binding.pitFileIndicator.setVisibility(View.VISIBLE);
binding.pitTeamName.setVisibility(View.VISIBLE);
binding.pitTeamDescription.setVisibility(View.VISIBLE);
binding.pitTeamName.setText(team.teamName);
binding.pitTeamDescription.setText(team.getDescription());
binding.pitBarTeamNum.setText(String.valueOf(team.teamNumber));
// binding.teamName.setText(team.teamName);
// binding.teamDescription.setText(team.getDescription());
filename = evcode + "-" + team.teamNumber + ".pitscoutdata";
boolean new_file = !fileEditor.fileExist(filename);
if(asm.isRunning){
asm.stop();
}
create_fields();
if(new_file){
default_fields();
set_indicator_color(unsaved_color);
}else{
get_fields();
set_indicator_color(saved_color);
}
asm.start();
}
private int default_text_color = 0;
private void create_fields() {
if(asm.isRunning){
asm.stop();
}
titles = new TextView[pit_latest_values.length];
for(int i = 0 ; i < pit_latest_values.length; i++) {
TextView tv = new TextView(getContext());
tv.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
tv.setText(pit_latest_values[i].name);
tv.setTextSize(24);
tv.setPadding(8,8,8,8);
titles[i] = tv;
binding.pitScoutArea.addView(tv);
default_text_color = tv.getCurrentTextColor();
int fi = i;
tv.setOnClickListener(p -> {
// boolean blank = !latest_values[fi].getViewValue().isNull();
// System.out.println(blank);
asm.update();
if(!pit_latest_values[fi].isBlank){
tv.setBackgroundColor(0xffff0000);
tv.setTextColor(0xff000000);
pit_latest_values[fi].nullify();
}else{
tv.setBackgroundColor(0x00000000);
tv.setTextColor(default_text_color);
pit_latest_values[fi].setViewValue(pit_latest_values[fi].default_value);
}
});
View v = pit_latest_values[i].createView(getContext(), new Function<dataType, Integer>() {
@Override
public Integer apply(dataType dataType) {
// edited = true;
if(asm.isRunning)
update_asm();
return 0;
}
});
binding.pitScoutArea.addView(v);
}
}
public void default_fields(){
for(int i = 0; i < pit_latest_values.length; i++){
inputType input = pit_latest_values[i];
input.setViewValue(input.default_value);
titles[i].setBackgroundColor(0x00000000);
titles[i].setTextColor(default_text_color);
}
}
public void get_fields(){
ScoutingDataWriter.ParsedScoutingDataResult psdr = ScoutingDataWriter.load(filename, pit_values, pit_transferValues);
dataType[] types = psdr.data.array;
for(int i = 0; i < pit_latest_values.length; i++){
// types[i] = latest_values[i].getViewValue();
pit_latest_values[i].setViewValue(types[i]);
if(pit_latest_values[i].isBlank){
titles[i].setBackgroundColor(0xffff0000);
titles[i].setTextColor(0xff000000);
}else{
titles[i].setBackgroundColor(0x00000000);
titles[i].setTextColor(default_text_color);
}
}
}
}
@@ -0,0 +1,106 @@
package com.ridgebotics.ridgescout.ui.scouting;
import static androidx.navigation.fragment.FragmentKt.findNavController;
import static com.ridgebotics.ridgescout.utility.DataManager.event;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.SettingsVersionStack.latestSettings;
import com.ridgebotics.ridgescout.databinding.FragmentScoutingBinding;
import com.ridgebotics.ridgescout.types.frcTeam;
import com.ridgebotics.ridgescout.ui.TeamSelectorFragment;
import com.ridgebotics.ridgescout.utility.DataManager;
public class ScoutingFragment extends Fragment {
private FragmentScoutingBinding binding;
private boolean is_main_page = true;
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentScoutingBinding.inflate(inflater, container, false);
binding.buttons.setVisibility(View.VISIBLE);
String evcode = latestSettings.settings.get_evcode();
if(evcode.equals("unset")){
binding.noEventError.setVisibility(View.VISIBLE);
binding.matchScoutingButton.setEnabled(false);
binding.pitScoutingButton.setEnabled(false);
binding.statusButton.setEnabled(false);
is_main_page = false;
return binding.getRoot();
}
DataManager.reload_event();
if(event.matches.isEmpty())
binding.matchScoutingButton.setVisibility(View.GONE);
binding.matchScoutingButton.setOnClickListener(v -> {
findNavController(this).navigate(R.id.action_navigation_scouting_to_navigation_match_scouting);
});
binding.pitScoutingButton.setOnClickListener(v -> {
TeamSelectorFragment.setPits_mode(true);
TeamSelectorFragment.setOnSelect(new TeamSelectorFragment.onTeamSelected() {
@Override
public void onSelect(TeamSelectorFragment self, frcTeam team) {
PitScoutingFragment.setTeam(team);
findNavController(self).navigate(R.id.action_navigation_team_selector_to_navigation_pit_scouting);
}
});
findNavController(this).navigate(R.id.action_navigation_scouting_to_navigation_team_selector);
});
binding.statusButton.setOnClickListener(v -> {
findNavController(this).navigate(R.id.action_navigation_scouting_to_navigation_scouting_status);
});
return binding.getRoot();
}
@Override
public void onResume() {
super.onResume();
if(getView() == null){
return;
}
getView().setFocusableInTouchMode(true);
getView().requestFocus();
getView().setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP
&& keyCode == KeyEvent.KEYCODE_BACK
&& !is_main_page){
// binding.buttons.setVisibility(View.VISIBLE);
// binding.matchScoutingView.setVisibility(View.GONE);
// binding.pitScoutingView.setVisibility(View.GONE);
is_main_page = true;
return true;
}
return false;
}
});
}
}
@@ -0,0 +1,165 @@
package com.ridgebotics.ridgescout.ui.scouting;
import static com.ridgebotics.ridgescout.utility.DataManager.event;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TableRow;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.databinding.FragmentScoutingStatusBinding;
import com.ridgebotics.ridgescout.utility.DataManager;
import com.ridgebotics.ridgescout.utility.fileEditor;
import com.ridgebotics.ridgescout.types.frcEvent;
import com.ridgebotics.ridgescout.types.frcMatch;
import java.util.Arrays;
public class StatusFragment extends Fragment {
FragmentScoutingStatusBinding binding;
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentScoutingStatusBinding.inflate(inflater, container, false);
DataManager.reload_event();
binding.matchTable.removeAllViews();
binding.matchTable.setStretchAllColumns(true);
add_pit_scouting(event);
add_match_scouting(event);
return binding.getRoot();
}
public static int color_found = 0x7f00ff00;
public static int color_not_found = 0x7f7f0000;
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 void add_pit_scouting(frcEvent event){
TextView tv = new TextView(getContext());
tv.setLayoutParams(new TableRow.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText("Pit Scouting");
tv.setTextSize(28);
binding.matchTable.addView(tv);
int[] teams = new int[event.teams.size()];
for(int i = 0 ; i < event.teams.size(); i++){
teams[i] = event.teams.get(i).teamNumber;
}
Arrays.sort(teams);
TableRow tr = null;
for(int i=0; i < event.teams.size(); i++){
// frcTeam team = event.teams.get(i);
int num = teams[i];
if(i % 7 == 0){
if(i != 0)
binding.matchTable.addView(tr);
tr = new TableRow(getContext());
}
TextView text = new TextView(getContext());
text.setTextSize(18);
text.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
text.setText(String.valueOf(num));
if(fileEditor.fileExist(event.eventCode + "-" + num + ".pitscoutdata")){
text.setBackgroundColor(color_found);
}else{
text.setBackgroundColor(color_not_found);
}
tr.addView(text);
}
if(tr != null)
binding.matchTable.addView(tr);
}
public void add_match_scouting(frcEvent event){
TextView tv = new TextView(getContext());
tv.setLayoutParams(new TableRow.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText("Match Scouting");
tv.setTextSize(28);
binding.matchTable.addView(tv);
TableRow tr = new TableRow(getContext());
addTableText(tr, "#");
addTableText(tr, "Red-1");
addTableText(tr, "Red-2");
addTableText(tr, "Red-3");
addTableText(tr, "Blue-1");
addTableText(tr, "Blue-2");
addTableText(tr, "Blue-3");
binding.matchTable.addView(tr);
for(frcMatch match : event.matches){
tr = new TableRow(getContext());
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;
if(i < 3){
team_num = match.redAlliance[i];
alliance_position = "red-"+(i+1);
}else{
team_num = match.blueAlliance[i-3];
alliance_position = "blue-"+(i-2);
}
text.setText(String.valueOf(team_num));
if(fileEditor.fileExist(event.eventCode + "-" + match.matchIndex + "-" + alliance_position + "-" + team_num + ".matchscoutdata")){
text.setBackgroundColor(color_found);
}else{
text.setBackgroundColor(color_not_found);
}
tr.addView(text);
}
// addTableText(tr, String.valueOf(match.matchIndex));
// addTableText(tr, String.valueOf(match.blueAlliance[0]));
// addTableText(tr, String.valueOf(match.blueAlliance[1]));
// addTableText(tr, String.valueOf(match.blueAlliance[2]));
// addTableText(tr, String.valueOf(match.redAlliance[0]));
// addTableText(tr, String.valueOf(match.redAlliance[1]));
// addTableText(tr, String.valueOf(match.redAlliance[2]));
// if (toggle) {
// tr.setBackgroundColor(0x30000000);
// }
//
// toggle = !toggle;
binding.matchTable.addView(tr);
}
}
}
@@ -0,0 +1,79 @@
package com.ridgebotics.ridgescout.ui.scouting;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.ridgebotics.ridgescout.R;
public class TallyCounterView extends LinearLayout {
private int count = 0;
private TextView countDisplay;
private Button minusButton;
private Button plusButton;
private OnCountChangedListener onCountChangedListener;
public interface OnCountChangedListener {
void onCountChanged(int newCount);
}
public TallyCounterView(Context context) {
super(context);
init(context);
}
public TallyCounterView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public TallyCounterView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
LayoutInflater.from(context).inflate(R.layout.view_tally_counter, this, true);
countDisplay = findViewById(R.id.count_display);
minusButton = findViewById(R.id.minus_button);
plusButton = findViewById(R.id.plus_button);
updateDisplay();
minusButton.setOnClickListener(v -> {
if(count > 0) {
count--;
updateDisplay();
}
});
plusButton.setOnClickListener(v -> {
count++;
updateDisplay();
});
}
private void updateDisplay() {
countDisplay.setText(String.valueOf(count));
if (onCountChangedListener != null) {
onCountChangedListener.onCountChanged(count);
}
}
public void setValue(int value) {
count = value;
updateDisplay();
}
public int getValue() {
return count;
}
public void setOnCountChangedListener(OnCountChangedListener listener) {
this.onCountChangedListener = listener;
}
}
@@ -0,0 +1,226 @@
package com.ridgebotics.ridgescout.ui.settings;
import android.app.AlertDialog;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.Spinner;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.databinding.FragmentSettingsBinding;
import com.ridgebotics.ridgescout.utility.fileEditor;
import com.ridgebotics.ridgescout.SettingsVersionStack.latestSettings;
import com.skydoves.powerspinner.IconSpinnerAdapter;
import com.skydoves.powerspinner.IconSpinnerItem;
import com.skydoves.powerspinner.OnSpinnerItemSelectedListener;
import com.skydoves.powerspinner.PowerSpinnerView;
import java.util.ArrayList;
import java.util.List;
public class settingsFragment extends Fragment {
private FragmentSettingsBinding binding;
private android.widget.ScrollView ScrollArea;
private android.widget.TableLayout Table;
private void setDropdownItems(Spinner dropdown, String[] items){
ArrayAdapter<String> adapter = new ArrayAdapter<>(requireActivity(), android.R.layout.simple_spinner_item, items);
dropdown.setAdapter(adapter);
}
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
binding = FragmentSettingsBinding.inflate(inflater, container, false);
View root = binding.getRoot();
EditText username = binding.username;
username.setText(latestSettings.settings.get_username());
username.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
latestSettings.settings.set_username(username.getText().toString());
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
public void onTextChanged(CharSequence s, int start, int before, int count) {}
});
PowerSpinnerView spinnerView = binding.eventDropdown;
List<IconSpinnerItem> iconSpinnerItems = new ArrayList<>();
String target_event_name = latestSettings.settings.get_evcode();
int target_index = -1;
ArrayList<String> evlist = fileEditor.getEventList();
for(int i = 0; i < evlist.size(); i++){
if(evlist.get(i).equals(target_event_name)){
target_index = i;
}
iconSpinnerItems.add(new IconSpinnerItem(evlist.get(i)));
}
IconSpinnerAdapter iconSpinnerAdapter = new IconSpinnerAdapter(spinnerView);
spinnerView.setSpinnerAdapter(iconSpinnerAdapter);
spinnerView.setItems(iconSpinnerItems);
// spinnerView.setLifecycleOwner(this);
if(!iconSpinnerItems.isEmpty() && target_index != -1){
spinnerView.selectItemByIndex(target_index);
}
spinnerView.setOnSpinnerItemSelectedListener(new OnSpinnerItemSelectedListener<IconSpinnerItem>() {
@Override
public void onItemSelected(int oldIndex, @Nullable IconSpinnerItem oldItem, int newIndex,
IconSpinnerItem newItem) {
latestSettings.settings.set_evcode(newItem.getText().toString());
}
});
PowerSpinnerView alliance_pos_spinnerView = binding.alliancePosDropdown;
List<IconSpinnerItem> alliance_pos_iconSpinnerItems = new ArrayList<>();
String target_alliance_pos = latestSettings.settings.get_alliance_pos();
int alliance_pos_target_index = -1;
String[] alliance_pos_list = new String[]{"red-1", "red-2", "red-3",
"blue-1", "blue-2", "blue-3"};
for(int i = 0; i < alliance_pos_list.length; i++){
if(alliance_pos_list[i].equals(target_alliance_pos)){
alliance_pos_target_index = i;
}
alliance_pos_iconSpinnerItems.add(new IconSpinnerItem(alliance_pos_list[i]));
}
IconSpinnerAdapter alliance_pos_iconSpinnerAdapter = new IconSpinnerAdapter(alliance_pos_spinnerView);
alliance_pos_spinnerView.setSpinnerAdapter(alliance_pos_iconSpinnerAdapter);
alliance_pos_spinnerView.setItems(alliance_pos_iconSpinnerItems);
alliance_pos_spinnerView.setLifecycleOwner(this);
if(alliance_pos_target_index != -1){
alliance_pos_spinnerView.selectItemByIndex(alliance_pos_target_index);
}
alliance_pos_spinnerView.setOnSpinnerItemSelectedListener(new OnSpinnerItemSelectedListener<IconSpinnerItem>() {
@Override
public void onItemSelected(int oldIndex, @Nullable IconSpinnerItem oldItem, int newIndex,
IconSpinnerItem newItem) {
latestSettings.settings.set_alliance_pos(newItem.getText().toString());
}
});
//
// CheckBox practice_mode = binding.practiceMode;
// practice_mode.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
// @Override
// public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
// latestSettings.settings.set_practice_mode(isChecked);
// }
//
// });
//
// practice_mode.setChecked(latestSettings.settings.get_practice_mode());
EditText team_num = binding.teamNumber;
team_num.setText(String.valueOf(latestSettings.settings.get_team_num()));
team_num.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
latestSettings.settings.set_team_num(team_num.getText().toString());
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
public void onTextChanged(CharSequence s, int start, int before, int count) {}
});
CheckBox wifi_mode = binding.wifiMode;
wifi_mode.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
latestSettings.settings.set_wifi_mode(isChecked);
}
});
wifi_mode.setChecked(latestSettings.settings.get_wifi_mode());
Button reset_button = binding.resetButton;
reset_button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AlertDialog.Builder alert = new AlertDialog.Builder(getContext());
alert.setTitle("Warning");
alert.setMessage("Do you really want to reset settings?");
alert.setCancelable(true);
alert.setPositiveButton("Ok", (dialog, which) -> {
latestSettings.settings.defaultSettings();
username.setText(latestSettings.settings.get_username());
spinnerView.clearSelectedItem();
// practice_mode.setChecked(latestSettings.settings.get_practice_mode());
wifi_mode.setChecked(latestSettings.settings.get_wifi_mode());
alliance_pos_spinnerView.selectItemByIndex(0);
team_num.setText(String.valueOf(latestSettings.settings.get_team_num()));
});
alert.setNegativeButton("Cancel", null);
alert.create().show();
}
});
return root;
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}
@@ -0,0 +1,117 @@
package com.ridgebotics.ridgescout.ui.transfer;
import static com.ridgebotics.ridgescout.utility.DataManager.evcode;
import static com.ridgebotics.ridgescout.utility.DataManager.event;
import static com.ridgebotics.ridgescout.utility.DataManager.match_latest_values;
import static com.ridgebotics.ridgescout.utility.DataManager.pit_latest_values;
import static com.ridgebotics.ridgescout.utility.SharePrompt.shareContent;
import android.content.Context;
import com.ridgebotics.ridgescout.scoutingData.ScoutingDataWriter;
import com.ridgebotics.ridgescout.types.data.dataType;
import com.ridgebotics.ridgescout.types.frcMatch;
import com.ridgebotics.ridgescout.types.frcTeam;
import com.ridgebotics.ridgescout.utility.DataManager;
import com.ridgebotics.ridgescout.utility.fileEditor;
public class CSVExport {
private static String[] alliances = {"red", "blue"};
public static void exportMatches(Context c){
DataManager.reload_event();
DataManager.reload_match_fields();
String data = "";
data += ("num,alliance,alliance_position,teamnum,");
for(int i = 0; i < match_latest_values.length; i++){
data += (match_latest_values[i].name + ",");
}
data += ("\n");
for(int matchNum = 1; matchNum <= event.matches.size(); matchNum++){
for(int allianceIndex = 0; allianceIndex <= 1; allianceIndex++){
String alliance = alliances[allianceIndex];
for(int alliancePos = 1; alliancePos <= 3; alliancePos++){
data += (matchNum + ",");
data += (alliance + ",");
data += (alliancePos + ",");
frcMatch match = event.matches.get(matchNum-1);
int teamNum = 0;
if(allianceIndex == 0){
teamNum = match.redAlliance[alliancePos-1];
}else{
teamNum = match.blueAlliance[alliancePos-1];
}
data += (teamNum + ",");
String filename = evcode+"-"+matchNum+"-"+alliance+"-"+alliancePos+"-"+teamNum+".matchscoutdata";
if(!fileEditor.fileExist(filename)){
data += ("null,".repeat(match_latest_values.length));
}else{
ScoutingDataWriter.ParsedScoutingDataResult psdr = ScoutingDataWriter.load(filename, DataManager.match_values, DataManager.match_transferValues);
dataType[] types = psdr.data.array;
for(int i = 0; i < types.length; i++) {
data += (types[i].get() + ",");
}
}
data += ("\n");
}}}
shareContent(c, evcode+"-matches.csv", data, "text/plain");
}
public static void exportPits(Context c){
DataManager.reload_event();
DataManager.reload_pit_fields();
String data = "";
data += ("teamnum,teamname,city,teamnum,stateOrProv,school,country,startingYear,");
for(int i = 0; i < pit_latest_values.length; i++){
data += (pit_latest_values[i].name + ",");
}
data += ("\n");
for(int teamIndex = 0; teamIndex < event.teams.size(); teamIndex++){
frcTeam team = event.teams.get(teamIndex);
data += (team.teamNumber + ",");
data += (team.teamName + ",");
data += (team.city + ",");
data += (team.stateOrProv + ",");
data += (team.school + ",");
data += (team.country + ",");
data += (team.startingYear + ",");
String filename = evcode+"-"+team.teamNumber+".pitscoutdata";
if(!fileEditor.fileExist(filename)){
data += ("null,".repeat(pit_latest_values.length));
}else{
ScoutingDataWriter.ParsedScoutingDataResult psdr = ScoutingDataWriter.load(filename, DataManager.pit_values, DataManager.pit_transferValues);
dataType[] types = psdr.data.array;
for(int i = 0; i < types.length; i++) {
data += (types[i].get() + ",");
}
}
data += ("\n");
}
// System.out.print(data);
shareContent(c, evcode+"-pits.csv", data, "text/plain");
}
}
@@ -0,0 +1,107 @@
package com.ridgebotics.ridgescout.ui.transfer;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import com.ridgebotics.ridgescout.MainActivity;
import com.ridgebotics.ridgescout.types.file;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder;
import com.ridgebotics.ridgescout.utility.DataManager;
import com.ridgebotics.ridgescout.utility.SharePrompt;
import com.ridgebotics.ridgescout.utility.fileEditor;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
public class FileBundle {
private static final Intent FILE_SELECT_CODE = new Intent();
public static void send(String[] files, Context c){
try {
ByteBuilder b = new ByteBuilder();
for(int i = 0; i < files.length; i++){
if(!fileEditor.fileExist(files[i])) continue;
// byte[] data = fileEditor.readFile(files[i]);
file f = new file(files[i]);
b.addRaw(file.typecode, f.encode());
}
byte[] data = b.build();
send(data, c);
} catch (ByteBuilder.buildingException e) {
AlertManager.error(e);
}
}
public static void send(byte[] data, Context c){
String filename = DataManager.getevcode() + "-" + System.currentTimeMillis() + ".scoutbundle";
SharePrompt.shareContent(c, filename, data, "application/ridgescout");
}
public static void receive(Activity b){
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
MainActivity.setResultRelay(new MainActivity.activityResultRelay() {
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
Uri uri = data.getData();
if(uri == null) return;
try (InputStream is = b.getContentResolver().openInputStream(uri)) {
ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
int bufferSize = 1024;
byte[] buffer = new byte[bufferSize];
int len = 0;
while ((len = is.read(buffer)) != -1) {
byteBuffer.write(buffer, 0, len);
}
byte[] bytes = byteBuffer.toByteArray();
saveFiles(bytes);
// AlertManager.error(""+(bytes.length));
} catch (IOException e) {
// Handle the exception
}
}
});
b.startActivityForResult(intent, 1);
}
private static void saveFiles(byte[] data){
BuiltByteParser bbp = new BuiltByteParser(data);
try{
List<BuiltByteParser.parsedObject> parsedObjectList = bbp.parse();
ArrayList<String> filenames = new ArrayList<>();
for(int i = 0; i < parsedObjectList.size(); i++){
BuiltByteParser.parsedObject pa = parsedObjectList.get(i);
if(pa.getType() != file.typecode) continue;
file f = file.decode((byte[]) pa.get());
if(f == null) continue;
filenames.add(f.filename);
fileEditor.writeFile(f.filename, f.data);
}
AlertManager.alert("Saved",
String.join("\n", filenames));
}catch (BuiltByteParser.byteParsingExeption e){
AlertManager.error(e);
}
}
}
@@ -0,0 +1,189 @@
package com.ridgebotics.ridgescout.ui.transfer;
import static com.ridgebotics.ridgescout.utility.DataManager.evcode;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.databinding.FragmentTransferFileSelectorBinding;
import com.ridgebotics.ridgescout.types.file;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.ByteBuilder;
import com.ridgebotics.ridgescout.utility.fileEditor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class FileSelectorFragment extends Fragment {
private static final int background_color = 0x5000ff00;
private static final int unselected_background_color = 0x2000ff00;
private static on_file_select onSelect = files -> {};
public static void setOnSelect(on_file_select tmp){onSelect = tmp;}
public interface on_file_select {
void onSelect(byte[] data);
}
FragmentTransferFileSelectorBinding binding;
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
binding = FragmentTransferFileSelectorBinding.inflate(inflater, container, false);
meta_string_array = new String[]{
"matches.fields",
"pits.fields",
evcode+".eventdata"
};
String[] files = fileEditor.getEventFiles(evcode);
Boolean[] selected_arr = new Boolean[files.length];
Arrays.fill(selected_arr, Boolean.TRUE);
for(int i = 0; i < files.length; i++){
TableRow tr = new TableRow(getContext());
TableLayout.LayoutParams rowParams = new TableLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT
);
rowParams.setMargins(20,20,20,20);
tr.setLayoutParams(rowParams);
tr.setPadding(20,20,20,20);
binding.fileSelectorTable.addView(tr);
tr.setBackgroundColor(background_color);
TextView tv = new TextView(getContext());
tv.setText(String.valueOf(files[i]));
tv.setTextSize(20);
tr.addView(tv);
final int fi = i;
tr.setOnClickListener(view -> {
boolean sel = !selected_arr[fi];
selected_arr[fi] = sel;
tr.setBackgroundColor(sel ? background_color : unselected_background_color);
});
}
binding.fileSelectorSearchbar.setOnKeyListener((view, a, keyEvent) -> {
String search_param = binding.fileSelectorSearchbar.getText().toString();
List<Integer> match_num_nums = get_matches_from_search_params(search_param);
Arrays.fill(selected_arr, Boolean.TRUE);
for(int i = 0; i < files.length; i++){
View child = binding.fileSelectorTable.getChildAt(i);
child.setBackgroundColor(background_color);
child.setVisibility(is_in_search_param(files[i], search_param, match_num_nums) ? View.VISIBLE : View.GONE);
}
return false;
});
binding.fileSelectorButton.setText("Send");
binding.fileSelectorButton.setOnClickListener(view -> {
List<String> filenames = new ArrayList<>();
for(int i = 0; i < files.length; i++){
View child = binding.fileSelectorTable.getChildAt(i);
if(child.getVisibility() == View.VISIBLE && selected_arr[i])
filenames.add(files[i]);
}
onSelect.onSelect(get_bytes_of_filenames(filenames));
});
return binding.getRoot();
}
private static String[] meta_string_array;
private boolean is_in_search_param(String filename, String search_param, List<Integer> nums){
return
("meta".contains(search_param) && Arrays.asList(meta_string_array).contains(filename)) ||
filename.contains(search_param) ||
match_file_is_match_num(filename, nums);
}
private boolean match_file_is_match_num(String filename, List<Integer> nums){
if(!filename.endsWith(".matchscoutdata")) return false;
String[] dash_split = filename.split("-");
if(dash_split.length != 5) return false;
String s = dash_split[1];
if(!is_int(s)) return false;
int n = Integer.parseInt(s);
return nums.contains(n);
}
private List<Integer> get_matches_from_search_params(String search_param){
List<Integer> nums = new ArrayList<>();
String[] comma_split = search_param.split(",");
for(int i = 0; i < comma_split.length; i++){
if(comma_split[i].contains("-")){
String[] dash_split = comma_split[i].split("-");
if(dash_split.length != 2) continue;
String stra = dash_split[0];
String strb = dash_split[1];
if(!(is_int(stra) && is_int(strb))) continue;
int a = Integer.parseUnsignedInt(stra);
int b = Integer.parseUnsignedInt(strb);
for(int x = a; x <= b; x++)
nums.add(x);
} else if(is_int(comma_split[i]))
nums.add(Integer.parseUnsignedInt(comma_split[i]));
}
return nums;
}
private boolean is_int(String num){
try {
Integer.parseUnsignedInt(num);
return true;
}
catch (NumberFormatException e) {
return false;
}
}
private static byte[] get_bytes_of_filenames(List<String> filenames){
try {
ByteBuilder b = new ByteBuilder();
for(int i = 0; i < filenames.size(); i++){
file f = new file(filenames.get(i));
b.addRaw(file.typecode, f.encode());
}
return b.build();
} catch (ByteBuilder.buildingException e){
AlertManager.error(e);
return null;
}
}
}
@@ -0,0 +1,504 @@
package com.ridgebotics.ridgescout.ui.transfer;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.databinding.FragmentTransferTbaBinding;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.RequestTask;
import com.ridgebotics.ridgescout.types.frcEvent;
import com.ridgebotics.ridgescout.types.frcMatch;
import com.ridgebotics.ridgescout.types.frcTeam;
import com.ridgebotics.ridgescout.utility.fileEditor;
import com.ridgebotics.ridgescout.utility.JSONUtil;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
public class TBAFragment extends Fragment {
private final String TBAAddress = "https://www.thebluealliance.com/api/v3/";
private final String TBAHeader = "X-TBA-Auth-Key: tjEKSZojAU2pgbs2mBt06SKyOakVhLutj3NwuxLTxPKQPLih11aCIwRIVFXKzY4e";
private android.widget.TableLayout Table;
private FragmentTransferTbaBinding binding;
private static final int year = 2024;
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentTransferTbaBinding.inflate(inflater, container, false);
Table = binding.matchTable;
Table.setStretchAllColumns(true);
TableRow tr = new TableRow(getContext());
addTableText(tr, "Loading Events...");
Table.addView(tr);
final RequestTask rq = new RequestTask();
rq.onResult(s -> {
eventTable(s);
return null;
});
rq.execute(TBAAddress + "events/"+year, TBAHeader);
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 void eventTable(String dataString){
Table.removeAllViews();
Table.setStretchAllColumns(true);
Table.bringToFront();
Date currentTime = Calendar.getInstance().getTime();
try {
JSONArray data = new JSONArray(dataString);
Table.setStretchAllColumns(true);
Table.bringToFront();
boolean toggle = false;
for(int i=0;i<data.length();i++){
TableRow tr = new TableRow(getContext());
TableLayout.LayoutParams rowParams = new TableLayout.LayoutParams(
TableRow.LayoutParams.WRAP_CONTENT,
TableRow.LayoutParams.WRAP_CONTENT
);
rowParams.setMargins(20,20,20,20);
tr.setLayoutParams(rowParams);
tr.setPadding(20,20,20,20);
tr.setBackgroundColor(0x30000000);
JSONObject j = data.getJSONObject(i);
String matchKey = j.getString("key");
String name = j.getString("short_name");
// Sometimes, a short name is not present on TBA Events
if(name.isEmpty()){
name = j.getString("name");
}
TextView tv = new TextView(getContext());
tv.setGravity(Gravity.CENTER_VERTICAL);
tv.setTextSize(12);
tv.setText(j.getString("key"));
tr.addView(tv);
tv = new TextView(getContext());
tv.setTextSize(18);
tv.setText(name);
tr.addView(tv);
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
try {
Date startDate = format.parse(j.getString("start_date"));
Date endDate = format.parse(j.getString("end_date"));
if(currentTime.after(endDate)){
tr.setBackgroundColor(0x30FF0000);
}else if(currentTime.before(startDate)){
tr.setBackgroundColor(0x3000FF00);
}else if(currentTime.after(startDate) && currentTime.before(endDate)){
tr.setBackgroundColor(0x30FFFF00);
}
} catch (Exception e) {
AlertManager.error(e);
}
tr.setOnClickListener(v -> {
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, j);
return null;
});
rq1.execute((TBAAddress + "event/" + matchKey + "/matches"), TBAHeader);
return null;
});
rq.execute((TBAAddress + "event/" + matchKey + "/teams"), TBAHeader);
});
// tr.addView(cl);
Table.addView(tr, rowParams);
toggle = !toggle;
}
}catch (JSONException j){
AlertManager.alert("Error", "Invalid JSON");
}
}
static class matchComparator implements Comparator<JSONObject>
{
public int compare(JSONObject a, JSONObject b)
{
try {
return a.getInt("match_number") - b.getInt("match_number");
}catch (JSONException j){
return 0;
}
}
}
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();
final JSONArray teamData = new JSONArray(teamsString);
String matchKey = eventData.getString("key");
String matchName = eventData.getString("short_name");
// Sometimes, a short name is not present on TBA Events
if(matchName.isEmpty()){
matchName = eventData.getString("name");
}
// Event code at top
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
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
Button btn = new Button(getContext());
btn.setText("Save");
btn.setTextSize(18);
btn.setLayoutParams(new TableRow.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
Table.addView(btn);
if(teamData.length() == 0){
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){
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);
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);
}
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);
int[] teams = new int[teamData.length()];
for(int i = 0 ; i < teamData.length(); i++){
teams[i] = teamData.getJSONObject(i).getInt("team_number");
}
Arrays.sort(teams);
TableRow tr = null;
for(int i=0; i < teamData.length(); i++){
// frcTeam team = event.teams.get(i);
int num = teams[i];
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);
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);
final ArrayList<frcMatch> matchesOBJ = new ArrayList<>();
btn.setOnClickListener(v -> {
if(saveData(matchesOBJ, teamData, eventData)){
AlertManager.alert("Info", "Saved!");
}else{
AlertManager.alert("Error", "Error saving files.");
}
});
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");
addTableText(tr, "Red-2");
addTableText(tr, "Red-3");
addTableText(tr, "Blue-1");
addTableText(tr, "Blue-2");
addTableText(tr, "Blue-3");
Table.addView(tr);
if(matchData.length() == 0)
return;
final JSONArray sortedMatchData = JSONUtil.sort(matchData, (a, b) -> {
JSONObject ja = (JSONObject)a;
JSONObject jb = (JSONObject)b;
try {
return ja.getInt("match_number") - jb.getInt("match_number");
}catch (JSONException j){
return 0;
}
});
boolean toggle = false;
int matchCount = 1;
for(int a=0;a<sortedMatchData.length();a++){
final JSONObject match = sortedMatchData.getJSONObject(a);
if(!match.getString("comp_level").equals("qm")){
continue;
}
final JSONObject alliances = match.getJSONObject("alliances");
final JSONArray redAlliance = alliances.getJSONObject("red").getJSONArray("team_keys");
final JSONArray blueAlliance = alliances.getJSONObject("blue").getJSONArray("team_keys");
tr = new TableRow(getContext());
if (toggle) {
tr.setBackgroundColor(0x30000000);
}
addTableText(tr, String.valueOf(matchCount));
// addTableText(tr, match.getString("key"));
int[] blueKeys = new int[3];
int[] redKeys = new int[3];
for(int b=0;b<6;b++){
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.setText(str);
text.setBackgroundColor(0x50ff0000);
}else{
String str = blueAlliance.getString(b-3).substring(3);
blueKeys[b-3] = Integer.parseInt(str);
text.setText(str);
text.setBackgroundColor(0x500000ff);
}
}
Table.addView(tr);
frcMatch matchOBJ = new frcMatch();
matchOBJ.matchIndex = matchCount;
matchOBJ.blueAlliance = blueKeys;
matchOBJ.redAlliance = redKeys;
matchesOBJ.add(matchOBJ);
matchCount += 1;
toggle = !toggle;
}
// btn.setOnClickListener(v -> {
// if(saveData(matchesOBJ, teamData, eventData)){
// alert("Info", "Saved!");
// }else{
// alert("Error", "Error saving files.");
// }
// });
}catch (JSONException j){
AlertManager.error(j);
AlertManager.alert("Error", "Invalid JSON");
}
}
private boolean saveData(ArrayList<frcMatch> matchData, JSONArray teamData, JSONObject eventData){
try {
final String matchKey = eventData.getString("key");
String matchName = eventData.getString("short_name");
// Sometimes, a short name is not present on TBA Events
if(matchName.isEmpty()){
matchName = eventData.getString("name");
}
ArrayList<frcTeam> teams = new ArrayList<>();
for(int i=0;i<teamData.length();i++){
frcTeam teamObj = new frcTeam();
JSONObject team = teamData.getJSONObject(i);
teamObj.teamNumber = team.getInt("team_number");
teamObj.teamName = team.getString("nickname");
teamObj.city = team.getString("city");
teamObj.stateOrProv = team.getString("state_prov");
teamObj.school = team.getString("school_name");
teamObj.country = team.getString("country");
teamObj.startingYear = team.getInt("rookie_year");
teams.add(teamObj);
}
frcEvent event = new frcEvent();
event.name = matchName;
event.eventCode = matchKey;
event.teams = teams;
event.matches = matchData;
return fileEditor.setEvent(event);
}catch (JSONException j){
AlertManager.error(j);
AlertManager.alert("Error", "Invalid JSON");
return false;
}
}
}
@@ -0,0 +1,164 @@
package com.ridgebotics.ridgescout.ui.transfer;
import static androidx.navigation.fragment.FragmentKt.findNavController;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.SettingsVersionStack.latestSettings;
import com.ridgebotics.ridgescout.databinding.FragmentTransferBinding;
import com.ridgebotics.ridgescout.ui.transfer.bluetooth.BluetoothSenderFragment;
import com.ridgebotics.ridgescout.ui.transfer.codes.CodeGeneratorView;
public class TransferFragment extends Fragment {
private FragmentTransferBinding binding;
// private enum TransferTypes {
// CAMERA,
// BLUETOOTH,
// LOCAL_WIFI,
// SCOUTING_SERVER
// }
String evcode;
private static final int background_color = 0x5000ff00;
private static final int unselected_background_color = 0x2000ff00;
// private Bundle b;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
// b = savedInstanceState;
binding = FragmentTransferBinding.inflate(inflater, container, false);
evcode = latestSettings.settings.get_evcode();
binding.downloadButton.setOnClickListener(v -> {
start_download();
});
binding.TBAButton.setOnClickListener(v -> {
binding.noEventError.setVisibility(View.GONE);
AlertDialog.Builder alert = new AlertDialog.Builder(getContext());
alert.setTitle("Warning");
alert.setMessage("This action requires internet.");
alert.setCancelable(true);
alert.setPositiveButton("Ok", (dialog, which) -> {
findNavController(this).navigate(R.id.action_navigation_transfer_to_navigation_tba);
});
alert.setNegativeButton("Cancel", null);
alert.create().show();
});
if(evcode.equals("unset")){
binding.noEventError.setVisibility(View.VISIBLE);
binding.uploadButton.setEnabled(false);
binding.CSVButton.setEnabled(false);
binding.downloadButton.setEnabled(true);
return binding.getRoot();
}
binding.uploadButton.setOnClickListener(v -> {
start_upload();
});
binding.CSVButton.setOnClickListener(v -> {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle("Chose data");
builder.setNegativeButton("Pit data", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
CSVExport.exportPits(getContext());
}
});
builder.setPositiveButton("Match data", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
CSVExport.exportMatches(getContext());
}
});
builder.setNeutralButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
builder.show();
});
if(!latestSettings.settings.get_wifi_mode())
binding.TBAButton.setEnabled(false);
return binding.getRoot();
}
private void start_upload() {
FileSelectorFragment.setOnSelect(data -> {
TransferSelectorFragment.setOnSelect(new TransferSelectorFragment.onSelect() {
@Override
public void onSelectCodes(TransferSelectorFragment self) {
CodeGeneratorView.setData(data);
findNavController(self).navigate(R.id.action_navigation_transfer_selector_to_navigation_code_generator);
}
@Override
public void onSelectBluetooth(TransferSelectorFragment self) {
BluetoothSenderFragment.set_data(data);
findNavController(self).navigate(R.id.action_navigation_transfer_selector_to_navigation_bluetooth_sender);
}
@Override
public void onSelectFileBundle(TransferSelectorFragment self) {
FileBundle.send(data, getContext());
}
});
findNavController(this).navigate(R.id.action_navigation_file_selector_to_navigation_transfer_selector);
});
findNavController(this).navigate(R.id.action_navigation_transfer_to_navigation_file_selector);
}
private void start_download(){
TransferSelectorFragment.setOnSelect(new TransferSelectorFragment.onSelect() {
@Override
public void onSelectCodes(TransferSelectorFragment self) {
findNavController(self).navigate(R.id.action_navigation_transfer_selector_to_navigation_code_scanner);
}
@Override
public void onSelectBluetooth(TransferSelectorFragment self) {
findNavController(self).navigate(R.id.action_navigation_transfer_selector_to_navigation_bluetooth_receiver);
}
@Override
public void onSelectFileBundle(TransferSelectorFragment self) {
FileBundle.receive(getActivity());
}
});
findNavController(this).navigate(R.id.action_navigation_transfer_to_navigation_transfer_selector);
}
}
@@ -0,0 +1,47 @@
package com.ridgebotics.ridgescout.ui.transfer;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.databinding.FragmentTransferSelectorBinding;
public class TransferSelectorFragment extends Fragment {
// Declaring three blank funcs in one line lol
private static onSelect onselect = new onSelect() {@Override public void onSelectCodes(TransferSelectorFragment self) {}@Override public void onSelectBluetooth(TransferSelectorFragment self) {} @Override public void onSelectFileBundle(TransferSelectorFragment self) {}};
public static void setOnSelect(onSelect tmp) {
onselect = tmp;
}
public interface onSelect {
void onSelectCodes(TransferSelectorFragment self);
void onSelectBluetooth(TransferSelectorFragment self);
void onSelectFileBundle(TransferSelectorFragment self);
}
FragmentTransferSelectorBinding binding;
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
binding = FragmentTransferSelectorBinding.inflate(inflater, container, false);
binding.codesButton.setOnClickListener(view -> {
onselect.onSelectCodes(this);
});
binding.bluetoothButton.setOnClickListener(view -> {
onselect.onSelectBluetooth(this);
});
binding.fileBundleButton.setOnClickListener(view -> {
onselect.onSelectFileBundle(this);
});
return binding.getRoot();
}
}
@@ -0,0 +1,169 @@
package com.ridgebotics.ridgescout.ui.transfer.bluetooth;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.ridgebotics.ridgescout.utility.AlertManager;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class BluetoothReceiver {
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private static final String NAME = "BluetoothReceiverApp";
private static final int REQUEST_ENABLE_BT = 1;
private static final int REQUEST_PERMISSIONS = 2;
private Context context;
private BluetoothAdapter bluetoothAdapter;
private BluetoothServerSocket serverSocket;
private BluetoothSocket socket;
private InputStream inputStream;
private boolean listening = false;
private receivedData receiveddata;
public BluetoothReceiver(Context context, receivedData receiveddata) {
this.context = context;
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
this.receiveddata = receiveddata;
}
public boolean isBluetoothSupported() {
return bluetoothAdapter != null;
}
public boolean isBluetoothEnabled() {
return bluetoothAdapter != null && bluetoothAdapter.isEnabled();
}
private static final int REQUEST_BLUETOOTH_PERMISSIONS = 1;
public static void
requestBluetoothPermissions(Activity activity) {
List<String> permissionsNeeded = new ArrayList<>();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
// For Android 12 and above
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
permissionsNeeded.add(Manifest.permission.BLUETOOTH_SCAN);
}
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
permissionsNeeded.add(Manifest.permission.BLUETOOTH_CONNECT);
}
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.BLUETOOTH_ADVERTISE) != PackageManager.PERMISSION_GRANTED) {
permissionsNeeded.add(Manifest.permission.BLUETOOTH_ADVERTISE);
}
} else {
// For Android 11 and below
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.BLUETOOTH) != PackageManager.PERMISSION_GRANTED) {
permissionsNeeded.add(Manifest.permission.BLUETOOTH);
}
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.BLUETOOTH_ADMIN) != PackageManager.PERMISSION_GRANTED) {
permissionsNeeded.add(Manifest.permission.BLUETOOTH_ADMIN);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
permissionsNeeded.add(Manifest.permission.ACCESS_FINE_LOCATION);
}
}
}
if (!permissionsNeeded.isEmpty()) {
ActivityCompat.requestPermissions(activity, permissionsNeeded.toArray(new String[0]), REQUEST_BLUETOOTH_PERMISSIONS);
}
}
public static boolean hasBluetoothPermissions(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
return ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_ADVERTISE) == PackageManager.PERMISSION_GRANTED;
} else {
boolean hasBasicPermissions = ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH) == PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_ADMIN) == PackageManager.PERMISSION_GRANTED;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return hasBasicPermissions && ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
} else {
return hasBasicPermissions;
}
}
}
@SuppressLint("MissingPermission")
public void startListening() throws IOException {
if(!hasBluetoothPermissions(context)){
requestBluetoothPermissions((Activity) context);
return;
}
serverSocket = bluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
listening = true;
new Thread(new Runnable() {
@Override
public void run() {
while (listening) {
try {
socket = serverSocket.accept();
inputStream = socket.getInputStream();
// Handle incoming data here
handleIncomingData();
} catch (IOException e) {
AlertManager.error(e);
}
}
}
}).start();
}
private void handleIncomingData() throws IOException {
byte[] buffer = new byte[1024];
int bytes;
try {
while (true) {
bytes = inputStream.read(buffer);
if (bytes != -1) {
receiveddata.processReceivedData(buffer, bytes);
}
}
} catch (IOException e) {
if (e.getMessage() != null && e.getMessage().contains("bt socket closed, read return: -1")) {
receiveddata.onConnectionStop();
System.out.println("Bluetooth socket closed, treating as end of stream");
} else {
throw e;
}
}
}
public interface receivedData {
public void processReceivedData(byte[] data, int bytes);
public void onConnectionStop();
}
public void stopListening() throws IOException {
listening = false;
if (serverSocket != null) {
serverSocket.close();
}
if (socket != null) {
socket.close();
}
if (inputStream != null) {
inputStream.close();
}
}
}
@@ -0,0 +1,166 @@
package com.ridgebotics.ridgescout.ui.transfer.bluetooth;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.databinding.FragmentTransferBluetoothReceiverBinding;
import com.ridgebotics.ridgescout.types.file;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.fileEditor;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.DataFormatException;
public class BluetoothReceiverFragment extends Fragment {
private BluetoothReceiver bluetoothReceiver;
private Button startListeningButton;
private Button stopListeningButton;
private TextView statusTextView;
// private void alert(String title, String content) {
// AlertDialog.Builder dialog = new AlertDialog.Builder(getContext());
// dialog.setCancelable(true);
// dialog.setTitle(title);
// dialog.setMessage(content);
//
// final AlertDialog alert = dialog.create();
// alert.show();
//
// }
FragmentTransferBluetoothReceiverBinding binding;
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentTransferBluetoothReceiverBinding.inflate(inflater, container, false);
// bluetoothReceiver = new BluetoothReceiver(context);
bluetoothReceiver = new BluetoothReceiver(getContext(), new BluetoothReceiver.receivedData() {
@Override
public void processReceivedData(byte[] data, int bytes) {
receiveData(data, bytes);
}
@Override
public void onConnectionStop() {
finished_recieve();
}
});
startListeningButton = binding.startListeningButton;
stopListeningButton = binding.stopListeningButton;
statusTextView = binding.statusTextView;
if (!bluetoothReceiver.isBluetoothSupported()) {
AlertManager.error("Bluetooth is not supported on this device");
return binding.getRoot();
}
if (!bluetoothReceiver.isBluetoothEnabled()) {
AlertManager.error("Please enable Bluetooth");
}
startListeningButton.setOnClickListener(v -> {
startListening();
});
stopListeningButton.setOnClickListener(v -> {
stopListening();
});
return binding.getRoot();
}
private void startListening() {
try {
bluetoothReceiver.startListening();
statusTextView.setText("Listening for incoming connections...");
startListeningButton.setEnabled(false);
stopListeningButton.setEnabled(true);
recievedBytes = new ArrayList<>();
} catch (IOException e) {
AlertManager.error("Failed to start listening: " + e.getMessage());
}
}
private void stopListening() {
try {
bluetoothReceiver.stopListening();
statusTextView.setText("Not listening");
startListeningButton.setEnabled(true);
stopListeningButton.setEnabled(false);
} catch (IOException e) {
AlertManager.error("Failed to stop listening: " + e.getMessage());
}
}
private List<byte[]> recievedBytes;
private void receiveData(byte[] data, int bytes) {
byte[] newBytes = fileEditor.getByteBlock(data, 0, bytes);
System.out.println("Recieved " + bytes + " Bytes over bluetooth!");
recievedBytes.add(newBytes);
}
private void finished_recieve() {
String result_filenames = "";
try {
byte[] resultBytes = fileEditor.combineByteArrays(recievedBytes);
resultBytes = fileEditor.blockUncompress(resultBytes);
BuiltByteParser bbp = new BuiltByteParser(resultBytes);
ArrayList<BuiltByteParser.parsedObject> result = bbp.parse();
for (int i = 0; i < result.size(); i++) {
if (result.get(i).getType() != file.typecode) continue;
file f = file.decode((byte[]) result.get(i).get());
if (f != null) {
System.out.println(f.filename);
if (f.write())
result_filenames += f.filename + "\n";
}
}
} catch (DataFormatException e) {
AlertManager.error(e);
} catch (BuiltByteParser.byteParsingExeption e) {
AlertManager.error(e);
}
AlertManager.alert("Completed!", result_filenames);
}
@Override
public void onDestroy() {
if (bluetoothReceiver != null)
try {
bluetoothReceiver.stopListening();
} catch (IOException e) {
AlertManager.error(e);
}
super.onDestroy();
}
}
@@ -0,0 +1,140 @@
package com.ridgebotics.ridgescout.ui.transfer.bluetooth;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.ridgebotics.ridgescout.utility.AlertManager;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
public class BluetoothSender {
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private static final int REQUEST_ENABLE_BT = 1;
private static final int REQUEST_PERMISSIONS = 2;
private Context context;
private BluetoothAdapter bluetoothAdapter;
private BluetoothSocket socket;
public OutputStream outputStream;
public BluetoothSender(Context context) {
this.context = context;
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
}
public boolean isBluetoothSupported() {
return bluetoothAdapter != null;
}
public boolean isBluetoothEnabled() {
return bluetoothAdapter != null && bluetoothAdapter.isEnabled();
}
private static final int REQUEST_BLUETOOTH_PERMISSIONS = 1;
public static void requestBluetoothPermissions(Activity activity) {
List<String> permissionsNeeded = new ArrayList<>();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
// For Android 12 and above
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
permissionsNeeded.add(Manifest.permission.BLUETOOTH_SCAN);
}
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
permissionsNeeded.add(Manifest.permission.BLUETOOTH_CONNECT);
}
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.BLUETOOTH_ADVERTISE) != PackageManager.PERMISSION_GRANTED) {
permissionsNeeded.add(Manifest.permission.BLUETOOTH_ADVERTISE);
}
} else {
// For Android 11 and below
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.BLUETOOTH) != PackageManager.PERMISSION_GRANTED) {
permissionsNeeded.add(Manifest.permission.BLUETOOTH);
}
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.BLUETOOTH_ADMIN) != PackageManager.PERMISSION_GRANTED) {
permissionsNeeded.add(Manifest.permission.BLUETOOTH_ADMIN);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
permissionsNeeded.add(Manifest.permission.ACCESS_FINE_LOCATION);
}
}
}
if (!permissionsNeeded.isEmpty()) {
ActivityCompat.requestPermissions(activity, permissionsNeeded.toArray(new String[0]), REQUEST_BLUETOOTH_PERMISSIONS);
}
}
public static boolean hasBluetoothPermissions(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
return ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_ADVERTISE) == PackageManager.PERMISSION_GRANTED;
} else {
boolean hasBasicPermissions = ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH) == PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_ADMIN) == PackageManager.PERMISSION_GRANTED;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return hasBasicPermissions && ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
} else {
return hasBasicPermissions;
}
}
}
@SuppressLint("MissingPermission")
public Set<BluetoothDevice> getPairedDevices() {
if(!hasBluetoothPermissions(context)){
requestBluetoothPermissions((Activity) context);
return null;
}
return bluetoothAdapter.getBondedDevices();
}
@SuppressLint("MissingPermission")
public void connectToDevice(BluetoothDevice device) throws IOException {
if(!hasBluetoothPermissions(context)){
requestBluetoothPermissions((Activity) context);
return;
}
socket = device.createRfcommSocketToServiceRecord(MY_UUID);
socket.connect();
outputStream = socket.getOutputStream();
}
public void sendData(byte[] data) throws IOException {
if (outputStream != null) {
outputStream.write(data);
}
}
public void close() throws IOException {
if (outputStream != null) {
outputStream.flush();
outputStream.close();
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
AlertManager.error(e);
}
if (socket != null) {
socket.close();
}
}
}
@@ -0,0 +1,130 @@
package com.ridgebotics.ridgescout.ui.transfer.bluetooth;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothDevice;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.databinding.FragmentTransferBluetoothSenderBinding;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.fileEditor;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Set;
public class BluetoothSenderFragment extends Fragment {
private BluetoothSender bluetoothSender;
private ListView deviceListView;
private Button sendFileButton;
private ArrayAdapter<String> deviceArrayAdapter;
private ArrayList<BluetoothDevice> deviceList;
private FragmentTransferBluetoothSenderBinding binding;
private static byte[] data_to_send = new byte[0];
public static void set_data(byte[] data){
data_to_send = data;
}
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentTransferBluetoothSenderBinding.inflate(inflater, container, false);
bluetoothSender = new BluetoothSender(getContext());
deviceListView = binding.deviceListView;
sendFileButton = binding.sendFileButton;
deviceList = new ArrayList<>();
deviceArrayAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_list_item_1);
deviceListView.setAdapter(deviceArrayAdapter);
if (!bluetoothSender.isBluetoothSupported()) {
AlertManager.toast("Bluetooth is not supported on this device");
return binding.getRoot();
}
if (!bluetoothSender.isBluetoothEnabled()) {
AlertManager.toast("Please enable Bluetooth");
} else {
listPairedDevices();
}
deviceListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@SuppressLint("MissingPermission")
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
BluetoothDevice selectedDevice = deviceList.get(position);
try {
bluetoothSender.connectToDevice(selectedDevice);
AlertManager.toast("Connected to " + selectedDevice.getName());
sendFileButton.setEnabled(true);
} catch (IOException e) {
AlertManager.toast("Failed to connect: " + e.getMessage());
}
}
});
sendFileButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sendData();
}
});
return binding.getRoot();
}
@SuppressLint("MissingPermission")
private void listPairedDevices() {
Set<BluetoothDevice> pairedDevices = bluetoothSender.getPairedDevices();
if (pairedDevices != null && !pairedDevices.isEmpty()) {
deviceList.addAll(pairedDevices);
for (BluetoothDevice device : pairedDevices) {
deviceArrayAdapter.add(device.getName() + "\n" + device.getAddress());
}
} else {
AlertManager.toast("No paired devices found");
}
}
private void sendData() {
try {
byte[] compressed = fileEditor.blockCompress(data_to_send);
for(int i = 0; i < Math.ceil((double) compressed.length/1024); i++){
bluetoothSender.sendData(fileEditor.getByteBlock(compressed, i*1024, (i+1)*1024));
}
bluetoothSender.close();
sendFileButton.setEnabled(false);
AlertManager.toast("File sent successfully");
} catch (IOException e) {
AlertManager.toast("Failed to send file: " + e.getMessage());
}
}
public void onDestroy() {
if (bluetoothSender != null)
try {
bluetoothSender.close();
} catch (IOException e) {
AlertManager.error(e);
}
super.onDestroy();
}
}
@@ -0,0 +1,237 @@
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.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
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.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.EnumMap;
import java.util.Map;
import java.util.Random;
public class CodeGeneratorView extends Fragment {
private ImageView qrImage;
private SeekBar qrSpeedSlider;
private SeekBar qrSizeSlider;
private TextView qrIndexN;
private TextView qrIndexD;
private final int maxQrCount = 256; //The max number that can be stored in a byte
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 final int defaultQrDelay = 419;
private int qrDelay = 0;
private int qrIndex = 0;
private CountDownTimer timer;
private int qrCount = 0;
private ArrayList<Bitmap> qrBitmaps;
private FragmentTransferCodeSenderBinding binding;
private static byte[] data;
public static void setData(String data){
setData(data.getBytes(StandardCharsets.ISO_8859_1));
}
public static void setData(byte[] tmpdata){
data = tmpdata;
}
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentTransferCodeSenderBinding.inflate(inflater, container, false);
qrImage = binding.qrImage;
qrSpeedSlider = binding.qrSpeedSlider;
qrSizeSlider = binding.qrSizeSlider;
qrIndexN = binding.qrIndexN;
qrIndexD = binding.qrIndexD;
String compressed = new String(fileEditor.blockCompress(data), StandardCharsets.ISO_8859_1);
if(compressed.isEmpty()){
AlertManager.alert("Error!", "Empty data!");
return binding.getRoot();
}
minQrSize = Math.round((float)compressed.length() / maxQrCount)+1;
qrSizeSlider.setMax(maxQrSize-minQrSize);
qrSpeedSlider.setMax((minQrSpeed-maxQrSpeed)*2);
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 = -(minQrSpeed - progress - maxQrSpeed + 1);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {}
});
qrSizeSlider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
qrSize = seekBar.getProgress() + minQrSize;
// qrCount = (int)Math.ceil((double) (data.length()+1)/qrSize);
qrCount = ((data.length()+1)/qrSize) +1;
qrIndexD.setText(String.valueOf(qrCount));
sendData(data);
}
});
// qrSizeSlider.setProgress(qr);
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) +
String.valueOf(fileEditor.byteToChar(randID)) +
fileEditor.byteToChar(i) +
fileEditor.byteToChar(qrCount - 1) +
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(){
qrImage.setImageBitmap(qrBitmaps.get(qrIndex));
if(qrDelay > 0) {
this.qrIndex += 1;
if (this.qrIndex >= this.qrCount) {
this.qrIndex = 0;
}
}else{
this.qrIndex -= 1;
if (this.qrIndex < 0) {
this.qrIndex = this.qrCount-1;
}
}
qrIndexN.setText(String.valueOf(qrIndex+1));
}
private void qrLoop(){
timer = new CountDownTimer(minQrSpeed-Math.abs(qrDelay)+1, 1000) {
public void onTick(long millisUntilFinished) {}
public void onFinish() {
updateQr();
qrLoop();
}
}.start();
}
}
@@ -0,0 +1,90 @@
package com.ridgebotics.ridgescout.ui.transfer.codes;
// From https://github.com/dlazaro66/QRCodeReaderView/blob/master/samples/src/main/java/com/example/qr_readerexample/PointsOverlayView.java
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.NonNull;
public class CodeOverlayView extends View {
PointF[] points;
int[] barColors;
private Paint paint;
private final int barHeight = 50;
public CodeOverlayView(Context context) {
super(context);
init();
}
public CodeOverlayView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CodeOverlayView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
paint = new Paint();
paint.setColor(Color.YELLOW);
paint.setStyle(Paint.Style.FILL);
}
public void setPoints(PointF[] points) {
this.points = points;
invalidate();
}
public void setBar(int[] barColors){
this.barColors = barColors;
invalidate();
}
@Override
public void draw(@NonNull Canvas canvas) {
super.draw(canvas);
if (points != null) {
for (PointF pointF : points) {
canvas.drawCircle(pointF.x, pointF.y, 10, paint);
}
}
if(barColors != null){
final double width = getWidth()/barColors.length;
final int top = 0;
final int bottom = barHeight;
for(int i=0;i<barColors.length;i++){
final int num = barColors[i];
int c = Color.RED;
if(num == 2){
c = Color.GREEN;
}else if(num == 1){
c = Color.YELLOW;
}
final Paint p = new Paint();
p.setColor(c);
canvas.drawRect(new Rect(
(int)(i*width), top,
(int)((i+1)*width), bottom
), p);
}
}
}
}
@@ -0,0 +1,67 @@
package com.ridgebotics.ridgescout.ui.transfer.codes;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.ChecksumException;
import com.google.zxing.DecodeHintType;
import com.google.zxing.FormatException;
import com.google.zxing.NotFoundException;
import com.google.zxing.RGBLuminanceSource;
import com.google.zxing.Reader;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.datamatrix.DataMatrixReader;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
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);
}
}
}
@@ -0,0 +1,345 @@
package com.ridgebotics.ridgescout.ui.transfer.codes;
import static androidx.core.math.MathUtils.clamp;
import android.app.AlertDialog;
import android.graphics.Bitmap;
import android.media.Image;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.widget.SeekBar;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.OptIn;
import androidx.camera.core.AspectRatio;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ExperimentalGetImage;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.ImageProxy;
import androidx.camera.core.Preview;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.LifecycleOwner;
import com.ridgebotics.ridgescout.databinding.FragmentTransferCodeReceiverBinding;
import com.ridgebotics.ridgescout.databinding.FragmentTransferCodeSenderBinding;
import com.ridgebotics.ridgescout.types.file;
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 java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//public class scannerView extends androidx.appcompat.widget.AppCompatImageView {
public class CodeScannerView extends Fragment {
private CodeOverlayView CodeOverlayView;
private Handler uiHandler;
private void alert(String title, String content) {
AlertDialog.Builder alert = new AlertDialog.Builder(getContext());
alert.setMessage(content);
alert.setTitle(title);
alert.setPositiveButton("OK", null);
alert.setCancelable(true);
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
int[] levelMap = new int[256];
private void recalcMap(){
for (int i = 0; i < 256; i++) {
levelMap[i] = clamp(
(clamp(
i-thresholdOffset, 0, 255) / (256 / numColors)) * (256 / numColors
)+brightness, 0, 255
);
}
}
private Bitmap toGreyscale(Image image){
// Turns out the "Y" In YUV is the Luminance of the pixel.
// Makes converting to greyscale 1000x easier
ByteBuffer yBuffer = image.getPlanes()[0].getBuffer();
final int width = image.getWidth();
final int height = image.getHeight();
int[] pixels = new int[width * height];
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;
}
}
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
return bitmap;
}
public void scanQRCode(Bitmap bitmap) {
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)
);
}
return null;
});
async.execute();
// return contents;
}
private int numColors = 3;
private int thresholdOffset = 128;
private int brightness = 128;
private FragmentTransferCodeReceiverBinding binding;
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentTransferCodeReceiverBinding.inflate(inflater, container, false);
this.lifecycle = getViewLifecycleOwner();
uiHandler = new Handler();
binding.scannerThreshold.setProgress(thresholdOffset);
binding.scannerThreshold.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
thresholdOffset = 127-progress;
recalcMap();
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {}
});
binding.scannerThreshold.setMax(255);
binding.scannerColors.setProgress(numColors);
binding.scannerColors.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
numColors = 18-(progress-2);
recalcMap();
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {}
});
binding.scannerColors.setMax(18);
binding.scannerBrightness.setProgress(brightness);
binding.scannerBrightness.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
brightness = progress-128;
recalcMap();
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {}
});
binding.scannerBrightness.setMax(256);
recalcMap();
CodeOverlayView = new CodeOverlayView(getContext());
CodeOverlayView.bringToFront();
ConstraintLayout.LayoutParams pointsOverlayViewParams = new ConstraintLayout.LayoutParams(
ConstraintLayout.LayoutParams.MATCH_PARENT, ConstraintLayout.LayoutParams.MATCH_PARENT
);
CodeOverlayView.setLayoutParams(pointsOverlayViewParams);
binding.container.addView(CodeOverlayView);
ListenableFuture<ProcessCameraProvider> cameraProviderFuture
= ProcessCameraProvider.getInstance(getContext());
cameraProviderFuture.addListener(() -> {
try {
ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
bindPreview(cameraProvider);
} catch (ExecutionException | InterruptedException e) {
// No errors need to be handled for this Future.
// This should never be reached.
}
}, ContextCompat.getMainExecutor(getContext()));
return binding.getRoot();
}
void bindPreview(@NonNull ProcessCameraProvider cameraProvider) {
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()
// .setTargetResolution(new Size(224, 224))
.setOutputImageRotationEnabled(false)
.setTargetAspectRatio(AspectRatio.RATIO_16_9)
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build();
imageAnalysis.setAnalyzer(executor, new ImageAnalysis.Analyzer() {
@OptIn(markerClass = ExperimentalGetImage.class) @Override
public void analyze(@NonNull ImageProxy image) {
Image img = Objects.requireNonNull(image.getImage());
uiHandler.post(new Runnable() {
final Bitmap bmp = toGreyscale(img);
@Override
public void run() {
// setImage(toGreyscale(bmp));
setImage(bmp);
}
});
image.close();
}
});
cameraProvider.unbindAll();
cameraProvider.bindToLifecycle(lifecycle,
cameraSelector, imageAnalysis, preview);
// preview.setSurfaceProvider(binding.previewView.getSurfaceProvider());
}
private String[] qrDataArr;
private int qrScannedCount;
private int[] barColors;
private int randID;
private int prevQrIndex;
private void compileData(int dataVersion, int randID, int qrIndex, int qrCount, String qrData){
if(dataVersion != fileEditor.internalDataVersion){
alert("Error", "Incorrect data version ("+dataVersion+" != "+fileEditor.internalDataVersion+")");
return;
}
// Reset code array if ID Changes
if(randID != this.randID){
this.randID = randID;
qrDataArr = new String[qrCount];
Log.i("title", ""+qrCount);
barColors = new int[qrCount];
prevQrIndex = qrIndex;
}
final boolean updated;
if(qrDataArr[qrIndex] == null) {
qrDataArr[qrIndex] = qrData;
updated = true;
qrScannedCount += 1;
}else{
updated = false;
}
barColors[prevQrIndex] = 2;
barColors[qrIndex] = 1;
CodeOverlayView.setBar(barColors);
if(updated && qrScannedCount >= qrCount){
String compiledString = "";
for(int i=0;i<qrCount;i++){
compiledString += qrDataArr[i];
}
try {
byte[] compiledBytes = compiledString.getBytes(StandardCharsets.ISO_8859_1);
byte[] resultBytes = fileEditor.blockUncompress(compiledBytes);
String result_filenames = "";
BuiltByteParser bbp = new BuiltByteParser(resultBytes);
ArrayList<BuiltByteParser.parsedObject> result = bbp.parse();
for(int i = 0; i < result.size(); i++){
if(result.get(i).getType() != file.typecode) continue;
file f = file.decode((byte[]) result.get(i).get());
if(f != null)
if(f.write())
result_filenames += f.filename + "\n";
}
AlertManager.alert("Completed!", result_filenames);
}catch (Exception e){
AlertManager.error(e);
}
}
prevQrIndex = qrIndex;
}
}
@@ -0,0 +1,72 @@
package com.ridgebotics.ridgescout.utility;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.widget.Toast;
import java.io.PrintWriter;
import java.io.StringWriter;
public class AlertManager {
public static Context context;
public static void init(Context c){
context = c;
}
public static void alert(String title, String content) {
((Activity) context).runOnUiThread(new Runnable() {
public void run() {
AlertDialog.Builder alert = new AlertDialog.Builder(context);
alert.setMessage(content);
alert.setTitle(title);
alert.setPositiveButton("OK", null);
alert.setCancelable(true);
alert.create().show();
}
});
}
public static void toast(String content) {
((Activity) context).runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(context, content, Toast.LENGTH_LONG).show();
}
});
}
public static void error(String content) {
((Activity) context).runOnUiThread(new Runnable() {
public void run() {
AlertDialog.Builder alert = new AlertDialog.Builder(context);
alert.setMessage(content);
alert.setTitle("Error!");
alert.setPositiveButton("OK", null);
alert.setCancelable(true);
alert.create().show();
}
});
}
public static void error(Exception e) {
e.printStackTrace();
((Activity) context).runOnUiThread(new Runnable() {
public void run() {
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
AlertDialog.Builder alert = new AlertDialog.Builder(context);
alert.setMessage(sw.toString());
alert.setTitle(e.getMessage());
alert.setPositiveButton("OK", null);
alert.setCancelable(true);
alert.create().show();
}
});
}
}
@@ -0,0 +1,75 @@
package com.ridgebotics.ridgescout.utility;
import android.os.Handler;
import android.os.Looper;
public class AutoSaveManager {
private static final long AUTO_SAVE_DELAY = 2000; // 2 seconds
private final Handler handler;
private final Runnable autoSaveRunnable;
private boolean isAutoSaveScheduled = false;
private final AutoSaveFunction autoSaveFunction;
public boolean isRunning = false;
// Functional interface for the auto-save function
@FunctionalInterface
public interface AutoSaveFunction {
void save();
}
public AutoSaveManager(AutoSaveFunction autoSaveFunction) {
this.autoSaveFunction = autoSaveFunction;
handler = new Handler(Looper.getMainLooper());
autoSaveRunnable = new Runnable() {
@Override
public void run() {
performAutoSave();
isAutoSaveScheduled = false;
}
};
}
public void start() {
isRunning = true;
}
public void stop() {
isRunning = false;
handler.removeCallbacks(autoSaveRunnable);
isAutoSaveScheduled = false;
}
public void update() {
if (!isRunning) {
return; // Don't schedule auto-saves when not running
}
// Cancel any previously scheduled auto-save
handler.removeCallbacks(autoSaveRunnable);
// Schedule a new auto-save
handler.postDelayed(autoSaveRunnable, AUTO_SAVE_DELAY);
isAutoSaveScheduled = true;
}
private void performAutoSave() {
if (isRunning) {
// Call the provided auto-save function
autoSaveFunction.save();
}
}
public void onDestroy() {
// Remove any pending auto-save tasks when the activity or fragment is destroyed
stop();
}
public boolean isAutoSaveScheduled() {
return isAutoSaveScheduled;
}
public boolean isRunning() {
return isRunning;
}
}
@@ -0,0 +1,159 @@
package com.ridgebotics.ridgescout.utility;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
public class BuiltByteParser {
public static final Integer boolType = 0;
public static final Integer intType = 1;
public static final Integer stringType = 2;
public static final Integer intArrayType = 3;
public static final Integer stringArrayType = 4;
public class byteParsingExeption extends Exception {
public byteParsingExeption() {}
public byteParsingExeption(String message) {
super(message);
}
}
public static String unBlankStrNull(String str){
if(str.equals("ƒ")){
return "";
}
else return str;
}
public abstract class parsedObject {
public abstract Integer getType();
public abstract Object get();
}
public class boolObject extends parsedObject{
boolean val;
public Integer getType(){return boolType;}
public Object get(){return val;}
}
public class intObject extends parsedObject{
int num;
public Integer getType(){return intType;}
public Object get(){return num;}
}
public class stringObject extends parsedObject{
String str;
public Integer getType(){return stringType;}
public Object get(){return str;}
}
public class intArrayObject extends parsedObject{
int[] arr;
public Integer getType(){return intArrayType;}
public Object get(){return arr;}
}
public class stringArrayObject extends parsedObject{
String[] arr;
public Integer getType(){return stringArrayType;}
public Object get(){return arr;}
}
public class rawObject extends parsedObject {
private int type;
public rawObject(int type){this.type = type;}
byte[] bytes;
public Integer getType(){return type;}
public Object get(){return bytes;}
}
byte[] bytes;
ArrayList<parsedObject> objects = new ArrayList<>();
public BuiltByteParser(byte[] bytes){
this.bytes = bytes;
}
public ArrayList<parsedObject> parse() throws byteParsingExeption {
if(bytes.length < 3){throw new byteParsingExeption("Invalid length");}
int curIndex = 0;
while(true){
// Log.i("t", String.valueOf(curIndex));
final int length = fileEditor.fromBytes(fileEditor.getByteBlock(bytes, curIndex, curIndex+2), 2);
final int type = bytes[curIndex+2] & 0xFF;
if(length == 0){
curIndex += 3;
continue;
}
final byte[] block;
try {
block = fileEditor.getByteBlock(bytes, curIndex + 3, curIndex + length + 3);
} catch(Exception e){
throw new byteParsingExeption("Array out of bounds");
}
switch(type){
case 0:
boolObject bo = new boolObject();
bo.val = block[0] == (byte) 1;
objects.add(bo);
break;
case 1:
intObject io = new intObject();
io.num = fileEditor.fromBytes(block, length);
objects.add(io);
break;
case 2:
stringObject so = new stringObject();
so.str = unBlankStrNull(new String(block, StandardCharsets.UTF_8));
objects.add(so);
break;
case 3:
BuiltByteParser int_bbp = new BuiltByteParser(block);
ArrayList<parsedObject> intArrayObjects = int_bbp.parse();
int[] intArr = new int[intArrayObjects.size()];
for(int i = 0; i < intArrayObjects.size(); i ++){
intArr[i] = (int) intArrayObjects.get(i).get();
}
intArrayObject ia = new intArrayObject();
ia.arr = intArr;
objects.add(ia);
break;
case 4:
BuiltByteParser str_bbp = new BuiltByteParser(block);
ArrayList<parsedObject> strArrayObjects = str_bbp.parse();
String[] StringArr = new String[strArrayObjects.size()];
for(int i = 0; i < strArrayObjects.size(); i ++){
StringArr[i] = (String) strArrayObjects.get(i).get();
}
stringArrayObject sa = new stringArrayObject();
sa.arr = StringArr;
objects.add(sa);
break;
default:
rawObject ro = new rawObject(type);
ro.bytes = block;
objects.add(ro);
break;
}
curIndex += length + 3;
if(curIndex == bytes.length){
break;
}else if(curIndex > bytes.length){
throw new byteParsingExeption("Block length problem");
}
}
return objects;
}
}
@@ -0,0 +1,214 @@
package com.ridgebotics.ridgescout.utility;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
public class ByteBuilder {
public static final int bool_id = 0;
public static final int int_id = 1;
public static final int string_id = 2;
public static final int int_arr_id = 3;
public static final int string_arr_id = 4;
ArrayList<byteType> bytesToBuild = new ArrayList<>();
public class buildingException extends Exception {
public buildingException() {}
public buildingException(String message) {
super(message);
}
}
private abstract static class byteType {
public abstract byte getType();
public abstract int length();
public abstract byte[] build();
}
public static String blankStrNull(String str){
if(str.isEmpty() || str.isEmpty()){
return "ƒ";
}
else return str;
}
private class boolType extends byteType {
public boolean val;
public byte getType(){return bool_id;}
public int length(){return 1;}
public byte[] build(){
return new byte[]{(byte) (val ? 1 : 0)};
}
}
public ByteBuilder addBool(boolean n) throws buildingException {
boolType boolType = new boolType();
boolType.val = n;
bytesToBuild.add(boolType);
return this;
}
private class intType extends byteType {
public int precision;
public int num;
public byte getType(){return int_id;}
public int length(){return precision;}
public byte[] build(){
return fileEditor.toBytes(num, precision);
}
}
private int getLeastBytePrecision(int num){
if(num <= 1){return 1;}
return (int) Math.ceil(Math.log(Math.abs(num))/Math.log(8));
}
public ByteBuilder addInt(int num) throws buildingException {
// Get closest number of bytes
int precision = getLeastBytePrecision(num);
return addInt(num, precision);
}
public ByteBuilder addInt(int num, int precision) throws buildingException {
if(precision <= 0){throw new buildingException("Invalid precision: " + precision);}
if(precision > 65536){throw new buildingException("Precision too large (greter than 65536)");}
if(precision < getLeastBytePrecision(num)){throw new buildingException("Precision too small");}
if(num > Integer.MAX_VALUE){throw new buildingException("Integer overflow");}
if(num < Integer.MIN_VALUE){throw new buildingException("Integer overflow");}
intType intType = new intType();
intType.num = num;
intType.precision = precision;
bytesToBuild.add(intType);
return this;
}
private class stringType extends byteType {
public byte[] bytes;
public byte getType(){return string_id;}
public int length(){return bytes.length;}
public byte[] build(){
return bytes;
// return str.getBytes(charset);
}
}
public ByteBuilder addString(String str) throws buildingException {
str = blankStrNull(str);
if(str.length() > 65536){throw new buildingException("String too long (greater than 65536)");}
stringType stringType = new stringType();
// To get the length correctly, the string bytes need to be precalculated
stringType.bytes = str.getBytes(StandardCharsets.UTF_8);
bytesToBuild.add(stringType);
return this;
}
private class intArrayType extends byteType {
public byte[] bytes;
public byte getType(){return int_arr_id;}
public int length(){return bytes.length;}
public byte[] build(){
return bytes;
// return str.getBytes(charset);
}
}
public ByteBuilder addIntArray(int[] arr) throws buildingException {
intArrayType intArrayType = new intArrayType();
ByteBuilder bb = new ByteBuilder();
for(int i = 0; i < arr.length; i++){
bb.addInt(arr[i]);
}
intArrayType.bytes = bb.build();
bytesToBuild.add(intArrayType);
return this;
}
private class stringArrayType extends byteType {
public byte[] bytes;
public byte getType(){return string_arr_id;}
public int length(){return bytes.length;}
public byte[] build(){
return bytes;
// return str.getBytes(charset);
}
}
public ByteBuilder addStringArray(String[] arr) throws buildingException {
stringArrayType stringArrayType = new stringArrayType();
ByteBuilder bb = new ByteBuilder();
for(int i = 0; i < arr.length; i++){
bb.addString(arr[i]);
}
stringArrayType.bytes = bb.build();
bytesToBuild.add(stringArrayType);
return this;
}
private class rawType extends byteType {
public int type;
public byte[] bytes;
public byte getType(){return (byte) type;}
public int length(){return bytes.length;}
public byte[] build(){
return bytes;
}
}
public ByteBuilder addRaw(int type, byte[] bytes) throws buildingException {
if(bytes.length > 65536){throw new buildingException("Byte array length to long (greater than 65536)");}
rawType rawType = new rawType();
rawType.type = type;
rawType.bytes = bytes;
bytesToBuild.add(rawType);
return this;
}
public byte[] build() throws buildingException {
if(bytesToBuild.size() == 0){throw new buildingException("Cannot build null data");}
int length = bytesToBuild.size() * 3;
for(byteType bt : bytesToBuild){
length += bt.length();
}
byte[] bytes = new byte[length];
int bytesFilled = 0;
for(byteType bt : bytesToBuild){
byte[] blockLength = fileEditor.toBytes(bt.length(), 2);
bytes[bytesFilled] = blockLength[0];
bytesFilled += 1;
bytes[bytesFilled] = blockLength[1];
bytesFilled += 1;
bytes[bytesFilled] = bt.getType();
bytesFilled += 1;
// if(bt.length == 0)
// continue;
int i = 1;
byte[] newBytes = bt.build();
for(byte b : newBytes){
// Log.i("i", (bytesFilled+1) + "/" + length + " (" + i + "/" + bt.length() + ")");
bytes[bytesFilled] = b;
bytesFilled += 1;
i++;
}
}
return bytes;
}
}
@@ -0,0 +1,38 @@
package com.ridgebotics.ridgescout.utility;
import com.ridgebotics.ridgescout.SettingsVersionStack.latestSettings;
import com.ridgebotics.ridgescout.scoutingData.fields;
import com.ridgebotics.ridgescout.scoutingData.transfer.transferType;
import com.ridgebotics.ridgescout.types.frcEvent;
import com.ridgebotics.ridgescout.types.input.inputType;
public class DataManager {
public static String evcode;
public static frcEvent event;
public static void reload_event(){
evcode = getevcode();
event = frcEvent.decode(fileEditor.readFile(evcode + ".eventdata"));
}
public static String getevcode() {
return latestSettings.settings.get_evcode();
}
public static inputType[][] match_values;
public static inputType[] match_latest_values;
public static transferType[][] match_transferValues;
public static void reload_match_fields(){
match_values = fields.load(fields.matchFieldsFilename);
match_latest_values = match_values[match_values.length-1];
match_transferValues = transferType.get_transfer_values(match_values);
}
public static inputType[][] pit_values;
public static inputType[] pit_latest_values;
public static transferType[][] pit_transferValues;
public static void reload_pit_fields(){
pit_values = fields.load(fields.pitsFieldsFilename);
pit_latest_values = pit_values[pit_values.length-1];
pit_transferValues = transferType.get_transfer_values(pit_values);
}
}
@@ -0,0 +1,23 @@
package com.ridgebotics.ridgescout.utility;
import org.json.JSONArray;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class JSONUtil {
public static JSONArray sort(JSONArray array, Comparator c){
List asList = new ArrayList(array.length());
for (int i=0; i<array.length(); i++){
asList.add(array.opt(i));
}
asList.sort(c);
JSONArray res = new JSONArray();
for (Object o : asList){
res.put(o);
}
return res;
}
}
@@ -0,0 +1,158 @@
package com.ridgebotics.ridgescout.utility;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import android.view.MotionEvent;
import android.widget.TableLayout;
public class ReorderableTableLayout extends TableLayout {
private boolean reorderingEnabled = false;
private int draggedRowIndex = -1;
private float lastY;
private List<View> rows;
private List<Integer> reorderedIndices;
private int rowHeight;
public ReorderableTableLayout(Context context) {
super(context);
init();
}
public ReorderableTableLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
rows = new ArrayList<>();
reorderedIndices = new ArrayList<>();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (!reorderingEnabled) {
return super.onInterceptTouchEvent(ev);
}
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
lastY = ev.getY();
draggedRowIndex = getRowIndexAtY(lastY);
if (draggedRowIndex != -1) {
View draggedRow = getChildAt(draggedRowIndex);
rowHeight = draggedRow.getHeight();
saveOriginalOrder();
return true;
}
break;
}
return super.onInterceptTouchEvent(ev);
}
@Override
public void addView(View child) {
super.addView(child);
reorderedIndices.add(reorderedIndices.size());
}
@Override
public void removeAllViews() {
super.removeAllViews();
reorderedIndices.clear();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!reorderingEnabled || draggedRowIndex == -1) {
return super.onTouchEvent(event);
}
float currentY = event.getY();
int targetIndex = getRowIndexAtY(currentY);
View child = getChildAt(targetIndex);
if(child != null)
getChildAt(targetIndex).callOnClick();
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
if (targetIndex != -1 && targetIndex != draggedRowIndex) {
updateRowOrder(draggedRowIndex, targetIndex);
draggedRowIndex = targetIndex;
}
lastY = currentY;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
draggedRowIndex = -1;
break;
}
return true;
}
private int getRowIndexAtY(float y) {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (y >= child.getTop() && y <= child.getBottom()) {
return i;
}
}
return -1;
}
private void saveOriginalOrder() {
rows.clear();
for (int i = 0; i < getChildCount(); i++) {
rows.add(getChildAt(i));
}
}
public void updateRowOrder(int fromIndex, int toIndex) {
saveOriginalOrder();
if (fromIndex < toIndex) {
for (int i = fromIndex; i < toIndex; i++) {
Collections.swap(rows, i, i + 1);
Collections.swap(reorderedIndices, i, i + 1);
}
} else {
for (int i = fromIndex; i > toIndex; i--) {
Collections.swap(rows, i, i - 1);
Collections.swap(reorderedIndices, i, i - 1);
}
}
removeAllViewsInLayout();
for (View view : rows) {
addViewInLayout(view, -1, view.getLayoutParams(), true);
}
requestLayout();
invalidate();
}
public void setReorderingEnabled(boolean enabled) {
reorderingEnabled = enabled;
}
public List<Integer> getReorderedIndexes() {
return reorderedIndices;
}
public void removeElement(int unshuffledindex){
System.out.println(Arrays.toString(new List[]{reorderedIndices}));
reorderedIndices.remove(unshuffledindex);
for (int i = 0; i < reorderedIndices.size(); i++) {
if(reorderedIndices.get(i) > unshuffledindex)
reorderedIndices.set(i, reorderedIndices.get(i) - 1);
}
System.out.println(Arrays.toString(new List[]{reorderedIndices}));
}
}
@@ -0,0 +1,77 @@
package com.ridgebotics.ridgescout.utility;
import android.os.AsyncTask;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.function.Function;
import javax.net.ssl.HttpsURLConnection;
public class RequestTask extends AsyncTask<String, String, String> {
private Function<String, String> resultFunction = null;
@Override
protected String doInBackground(String... uri) {
try {
URL url = new URL(uri[0]);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
String[] headers = uri[1].split(", ");
for(String header : headers){
String[] split = header.split(": ");
conn.setRequestProperty(split[0], split[1]);
}
if(conn.getResponseCode() == HttpsURLConnection.HTTP_OK){
// ByteArrayOutputStream out = new ByteArrayOutputStream();
return readStream(conn.getInputStream());
// Do normal input or output stream reading
}
else {
return null; // See documentation for more info on response handling
}
} catch (IOException e) {
AlertManager.error(e);
}
return null;
}
private static String readStream(InputStream in) {
BufferedReader reader = null;
StringBuilder response = new StringBuilder();
try {
reader = new BufferedReader(new InputStreamReader(in));
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
} catch (IOException e) {
AlertManager.error(e);
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
AlertManager.error(e);
}
}
}
return response.toString();
}
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);
}
}
}
@@ -0,0 +1,55 @@
package com.ridgebotics.ridgescout.utility;
import android.content.Context;
import org.tensorflow.lite.support.label.Category;
import org.tensorflow.lite.task.text.nlclassifier.NLClassifier;
import java.util.List;
public class SentimentAnalysis {
private static NLClassifier textClassifier;
public static void init(Context context){
try {
textClassifier = NLClassifier.createFromFile(context, "text_classification_v2.tflite");
} catch (Exception e) {
AlertManager.error(e);
}
}
public interface resultCallback {
public void onFinish(float data);
}
public static void analyse(String input, resultCallback result){
new Thread(new Runnable() {
@Override
public void run() {
List<Category> results = textClassifier.classify(input);
for(int i = 0; i < results.size(); i++){
Category cat = results.get(i);
switch (cat.getLabel()){
case "Positive":
result.onFinish(cat.getScore());
break;
}
}
}
}).start();
}
public static float analyse_sync(String input){
List<Category> results = textClassifier.classify(input);
for(int i = 0; i < results.size(); i++){
Category cat = results.get(i);
switch (cat.getLabel()){
case "Positive":
return cat.getScore();
}
}
return 0;
}
}
@@ -0,0 +1,37 @@
package com.ridgebotics.ridgescout.utility;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import androidx.core.content.FileProvider;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class SharePrompt {
public static void shareContent(Context context, String fileName, String content, String mimeType) {
shareContent(context, fileName, content.getBytes(), mimeType);
}
public static void shareContent(Context context, String fileName, byte[] content, String mimeType) {
try {
File file = new File(context.getCacheDir(), fileName);
FileOutputStream fos = new FileOutputStream(file);
fos.write(content);
fos.close();
Uri fileUri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", file);
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType(mimeType);
shareIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
context.startActivity(Intent.createChooser(shareIntent, "Share using"));
} catch (IOException e) {
AlertManager.error(e);
}
}
}
@@ -0,0 +1,371 @@
package com.ridgebotics.ridgescout.utility;
import android.content.Context;
import com.ridgebotics.ridgescout.types.frcEvent;
import com.ridgebotics.ridgescout.types.frcTeam;
import com.ridgebotics.ridgescout.SettingsVersionStack.latestSettings;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.BufferOverflowException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
public final class fileEditor {
private final static String baseDir = "/data/data/com.ridgebotics.ridgescout/";
public static final byte internalDataVersion = 0x01;
public static final int maxCompressedBlockSize = 4096;
public static String binaryVisualize(byte[] bytes){
String returnStr = "";
for (byte aByte : bytes) {
for (int b = 7; b >= 0; b--) {
returnStr += String.valueOf((aByte >> b) & 1);
}
returnStr += " (" + (int) aByte + ")\n";
}
return returnStr;
}
public static char byteToChar(int num){
return new String(toBytes(num, 1), StandardCharsets.ISO_8859_1).charAt(0);
}
public static byte[] toBytes(int num, int byteCount){
if(num > (Math.pow(2,byteCount*8)-1)){
throw new BufferOverflowException();
}
byte[] bytes = new byte[byteCount];
for(int i=0;i<byteCount;i++){
bytes[i] = (byte)(num >> (i*8));
}
return bytes;
}
public static int fromBytes(byte[] bytes, int byteCount){
int returnInt = 0;
for(int i=0;i<byteCount;i++){
returnInt |= (bytes[i] & 0xFF) << (i*8);
}
return returnInt;
}
public static int byteFromChar(char c){
byte[] bytes = (String.valueOf(c)).getBytes(Charset.defaultCharset());
return Byte.toUnsignedInt(bytes[0]);
}
public static byte[] getByteBlock(byte[] bytes, int start, int end){
end = Math.min(end, bytes.length);
byte[] dataBlock = new byte[end-start];
for(int a=start;a<end;a++){
// Log.i("test", start+", "+a+", "+end);
dataBlock[a-start] = bytes[a];
}
return dataBlock;
}
public static byte[] compress(byte[] input) {
Deflater deflater = new Deflater();
deflater.setInput(input);
deflater.finish();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[maxCompressedBlockSize];
while (!deflater.finished()) {
int compressedSize = deflater.deflate(buffer);
outputStream.write(buffer, 0, compressedSize);
}
return outputStream.toByteArray();
}
public static byte[] blockCompress(byte[] inputData) {
List<byte[]> compiledData = new ArrayList<>();
for(int i=0;i<Math.ceil((double) inputData.length / fileEditor.maxCompressedBlockSize);i++){
final int start = i*fileEditor.maxCompressedBlockSize;
int end = ((i+1)*fileEditor.maxCompressedBlockSize);
if(end > inputData.length) {
end = inputData.length;
}
byte[] dataBlock = fileEditor.getByteBlock(inputData, start, end);
final byte[] compressedBlock = fileEditor.compress(dataBlock);
compiledData.add(fileEditor.toBytes(compressedBlock.length, 2));
compiledData.add(compressedBlock);
}
return combineByteArrays(compiledData);
}
public static byte[] blockUncompress(byte[] data) throws DataFormatException {
List<byte[]> uncompressedData = new ArrayList<>();
int curIndex = 0;
while (curIndex < data.length) {
final int blockLength = fileEditor.fromBytes(fileEditor.getByteBlock(data, curIndex, curIndex + 2), 2);
uncompressedData.add(
decompress(
fileEditor.getByteBlock(data, curIndex + 2, curIndex + blockLength + 2)
)
);
curIndex += blockLength + 2;
}
return combineByteArrays(uncompressedData);
}
public static byte[] decompress(byte[] input) throws DataFormatException {
Inflater inflater = new Inflater();
inflater.setInput(input);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[maxCompressedBlockSize];
while (inflater.getRemaining() > 0) {
int decompressedSize = inflater.inflate(buffer);
if (decompressedSize == 0) {
break;
}
outputStream.write(buffer, 0, decompressedSize);
}
return outputStream.toByteArray();
}
public static byte[] combineByteArrays(List<byte[]> arrayList) {
// Calculate the total length of the combined array
int totalLength = arrayList.stream()
.mapToInt(array -> array.length)
.sum();
// Create a new byte array with the total length
byte[] result = new byte[totalLength];
// Copy each array into the result array
int offset = 0;
for (byte[] array : arrayList) {
System.arraycopy(array, 0, result, offset, array.length);
offset += array.length;
}
return result;
}
public static boolean writeFile(String filepath, byte[] data) {
try {
FileOutputStream output = new FileOutputStream(baseDir + filepath);
output.write(data);
output.close();
return true;
}
catch (IOException e) {
AlertManager.error(e);
return false;
}
}
public static boolean createFile(String filepath){
if(fileExist(filepath)){
return true;
}
try {
File file = new File(baseDir + filepath);
return file.createNewFile();
}
catch (IOException e) {
AlertManager.error(e);
return false;
}
}
public static boolean fileExist(String path){
File f = new File(baseDir + path);
return f.exists() && !f.isDirectory();
}
public static byte[] readFile(String path){
return readFileExact(baseDir + path);
}
public static byte[] readFileExact(String path){
File file = new File(path);
int size = (int) file.length();
byte[] bytes = new byte[size];
try {
BufferedInputStream buf = new BufferedInputStream(new FileInputStream(file));
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;
}
}
private static String intSplit(int[] intArr, String splitStr){
String returnStr = "";
for(int i=0;i<intArr.length;i++){
returnStr += String.valueOf(intArr[i]);
if(i != intArr.length-1){
returnStr += splitStr;
}
}
return returnStr;
}
public static boolean setEvent(frcEvent event){
final String filename = (event.eventCode + ".eventdata");
if(latestSettings.settings.get_evcode().equals("unset")){
latestSettings.settings.set_evcode(event.eventCode);
}
return writeFile(filename, event.encode());
}
public static ArrayList<String> getEventList(){
File f = new File(baseDir);
File[] files = f.listFiles();
ArrayList<String> outFiles = new ArrayList<>();
if(files == null){return outFiles;}
for (File file : files) {
if(!file.isDirectory() && file.getName().endsWith(".eventdata")) {
outFiles.add(file.getName().substring(0,file.getName().length()-10));
}
}
Collections.sort(outFiles);
return outFiles;
}
public static String[] getMatchesByTeamNum(String evcode, int teamNum){
File f = new File(baseDir);
File[] files = f.listFiles();
ArrayList<String> outFiles = new ArrayList<>();
if(files == null){return new String[0];}
for (File file : files) {
String name = file.getName();
if(!file.isDirectory() && name.startsWith(evcode+"-") && name.endsWith("-"+teamNum+".matchscoutdata")) {
outFiles.add(file.getName());
}
}
String[] filenames = outFiles.toArray(new String[0]);
Arrays.sort(filenames, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return Integer.valueOf(o1.split("-")[1]).compareTo(Integer.valueOf(o2.split("-")[1]));
}
});
return filenames;
}
public static String[] getEventFiles(String evcode){
File f = new File(baseDir);
File[] files = f.listFiles();
if(files == null){return new String[0];}
ArrayList<String> outFiles = new ArrayList<>();
outFiles.add("matches.fields");
outFiles.add("pits.fields");
// outFiles.add(evcode + ".eventdata");
for (File file : files) {
String name = file.getName();
if(!file.isDirectory() && name.startsWith(evcode)) {
outFiles.add(file.getName());
}
}
String[] filenames = outFiles.toArray(new String[0]);
Arrays.sort(filenames, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
try {
if(!o1.contains("-") || !o2.contains("-"))
return 0;
return Integer.valueOf(o1.split("-")[1]).compareTo(Integer.valueOf(o2.split("-")[1]));
} catch (Exception e){
return 0;
}
}
});
return filenames;
}
public static boolean setTeams(Context context, String key, ArrayList<frcTeam> teams){
return true;
}
}