12 Commits

Author SHA1 Message Date
Astatin3 fb0718c4ec Add FTP server 2024-10-06 16:06:43 -06:00
Astatin3 03a1507ce2 Make settings better, work on ftp transfer 2024-10-05 18:47:36 -06:00
Michael Mikovsky 2b753fcdb4 Fix README button 2024-10-01 07:34:27 -06:00
Astatin3 ca703aab60 Updata F-Droid metadata 2024-10-01 07:32:00 -06:00
Astatin3 4854587ea9 Add changes to README and TODO 2024-09-30 15:57:52 -06:00
Astatin3 d957a1db5e Temporarily disable AI stuff for ChiefDelphi Release 2024-09-28 12:50:38 -06:00
Astatin3 60d56473f5 Add more types of fields, F-droid fixes 2024-09-28 12:43:19 -06:00
Michael Mikovsky d607f95a8a Add images in README.md 2024-09-25 14:59:43 -06:00
Astatin3 62a4452581 Edit README, add Images 2024-09-25 14:49:47 -06:00
astatin3 b62d04dac5 Work on report menu 2024-09-22 11:06:40 -06:00
Astatin3 07c6e9a2aa Make use sharedprefrences 2024-09-20 15:59:35 -06:00
Astatin3 9f5ebd4215 AI intigration 2024-09-18 19:44:30 -06:00
62 changed files with 2463 additions and 952 deletions
+1
View File
@@ -1,6 +1,7 @@
# Gradle files # Gradle files
.gradle/ .gradle/
build/ build/
release/
# Local configuration file (sdk path, etc) # Local configuration file (sdk path, etc)
local.properties local.properties
Binary file not shown.
+34 -37
View File
@@ -1,40 +1,37 @@
# ScoutingApp2025 ![Ridgescout](https://github.com/Team4388/ScoutingApp2025/blob/main/metadata/en-US/images/featureGraphic.png?raw=true)
Ridgebotics 2025 scouting app in Android
## TODO: [<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
#### Scouting: alt="Get it on F-Droid"
- Make the "Report" menu, A tool that lets users select data to display from the the teams and compare menus. height="80">](https://f-droid.org/packages/com.ridgebotics.ridgescout/)
- Make practice mode?? **Note**: The F-Droid version of this app is not currently up to date with the GitHub release
#### Data Analysis:
- Statbotics intigration???
- AI overview of scouting data for a team???
- Make the "Compare" menu, cross comparing team's stats.
#### Functionality:
- Add more types of data fields.
- Test the scouting app
## In Progress: [**Read the wiki**](https://github.com/Team4388/ScoutingApp2025/wiki)
#### Scouting:
#### Data Analysis: [**Test Data**](https://github.com/Team4388/ScoutingApp2025/blob/main/2024week0-1728149849985.scoutbundle)
#### Functionality:
- Make server software to allow for easy sync over wifi - FTP #### Here is an overview of the main features currently included in the app:
- Deploy to F-Droid - This project is written for Android! No need for some kind of janky laptop charging setup.
- Similar to ScoutingPASS, there are many diffrent types of fields that can be used to collect data.
- The app is designed to handle updates to the fields on the fly, without loosing any data!
- Unlike other scouting solutions, scouters can disable any field they did not measure, and disabled fields will not be included in any calculations.
- Dynamic displays based off of the diffrent fields.
- Data transfer including 2D codes, Bluetooth, and File Bundle.
- Exporting using CSV.
- Deployment on F-Droid
- Data cloud sync using an FTP server
#### Things that are yet to be implemented:
- A page that lets users cross-compare scouting data between teams. (Compare)
- A page that lets scouters more easily make reports to the drive team before a match starts (Report)
#### Things that may or may not be implemented:
- Practice mode
- Statbotics intgration
- Scout error estimation using OPR-like calculation
- - Would most likely require Statbotics
### Screenshots
|Match scouting interface|Field editor|Teams data viewer|
|-|-|-|
|![Screenshot1](https://github.com/Team4388/ScoutingApp2025/blob/main/metadata/en-US/images/phoneScreenshots/1.png?raw=true)|![Screenshot2](https://github.com/Team4388/ScoutingApp2025/blob/main/metadata/en-US/images/phoneScreenshots/2.png?raw=true)|![Screenshot3](https://github.com/Team4388/ScoutingApp2025/blob/main/metadata/en-US/images/phoneScreenshots/3.png?raw=true)|
## Done:
#### Scouting:
- Add an "unselect" option to all of the scouting fields
- When a field is created, make updated scouting data return null values, not the default value
- Fix scouting offset bug
#### Data Analysis:
- Add "history" view type to the teams view menu.
- Sentiment analysis of text input type
- Add CSV exporting of match scouting data.
#### Functionality:
- Improve the code scanning progress indicator. It has a rounding error, I think.
- Fix navigation crashes.
- Make everything use Fragments instead of views that toggle visibility
- Make the file browser UI
- Bluetooth data sync
- Formalize error messages & stacktraces
- Make pit and match data field builder UIs. I don't want to have to keep editing a variable
- Make the system for blank and unselected fields better.
+38
View File
@@ -0,0 +1,38 @@
### TODO:
##### Scouting:
- Make practice mode??
##### Data Analysis:
- Statbotics intigration???
##### Functionality:
### In Progress:
##### Scouting:
##### Data Analysis:
- AI overview of scouting data for a team???
- Make the "Report" menu, A tool that lets users select data to display from the the teams and compare menus.
- Make the "Compare" menu, cross comparing team's stats.
##### Functionality:
### Done:
##### Scouting:
- Add an "unselect" option to all of the scouting fields
- When a field is created, make updated scouting data return null values, not the default value
- Fix scouting offset bug
##### Data Analysis:
- Add "history" view type to the teams view menu.
- Sentiment analysis of text input type
- Add CSV exporting of match scouting data.
##### Functionality:
- Improve the code scanning progress indicator. It has a rounding error, I think.
- Fix navigation crashes.
- Make everything use Fragments instead of views that toggle visibility
- Make the file browser UI
- Bluetooth data sync
- Formalize error messages & stacktraces
- Make pit and match data field builder UIs. I don't want to have to keep editing a variable
- Make the system for blank and unselected fields better.
- Write docs
- Deploy to F-Droid
- Add more types of data fields.
- Make server software to allow for easy sync over wifi - FTP
- Test the scouting app
+13 -5
View File
@@ -16,12 +16,20 @@ android {
namespace = "com.ridgebotics.ridgescout" namespace = "com.ridgebotics.ridgescout"
compileSdk = 34 compileSdk = 34
dependenciesInfo {
// Disables dependency metadata when building APKs.
includeInApk = false
// Disables dependency metadata when building Android App Bundles.
includeInBundle = false
}
defaultConfig { defaultConfig {
applicationId = "com.ridgebotics.ridgescout" applicationId = "com.ridgebotics.ridgescout"
minSdk = 24 minSdk = 24
targetSdk = 34 targetSdk = 34
versionCode = 3 versionCode = 5
versionName = "0.3" versionName = "0.5"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }
@@ -53,6 +61,7 @@ dependencies {
implementation(libs.lifecycle.viewmodel.ktx) implementation(libs.lifecycle.viewmodel.ktx)
implementation(libs.navigation.fragment) implementation(libs.navigation.fragment)
implementation(libs.navigation.ui) implementation(libs.navigation.ui)
implementation(libs.preference)
// implementation(libs.support.annotations) // implementation(libs.support.annotations)
testImplementation(libs.junit) testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit) androidTestImplementation(libs.ext.junit)
@@ -71,14 +80,13 @@ dependencies {
implementation("com.github.PhilJay:MPAndroidChart:v3.1.0") implementation("com.github.PhilJay:MPAndroidChart:v3.1.0")
// implementation("com.google.firebase:firebase-ml-modeldownloader:24.1.2") // implementation("com.google.firebase:firebase-ml-modeldownloader:24.1.2")
// implementation(platform("com.google.firebase:firebase-bom:33.1.2")) // implementation(platform("com.google.firebase:firebase-bom:33.1.2"))
implementation("org.tensorflow:tensorflow-lite-task-text:0.3.0") implementation("org.tensorflow:tensorflow-lite-task-text:0.3.0")
implementation("io.github.ollama4j:ollama4j:1.0.79") implementation("com.squareup.okhttp3:okhttp:4.9.0")
implementation("commons-net:commons-net:3.10.0")
Binary file not shown.
Binary file not shown.
-37
View File
@@ -1,37 +0,0 @@
{
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.ridgebotics.ridgescout",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 2,
"versionName": "0.2",
"outputFile": "app-release.apk"
}
],
"elementType": "File",
"baselineProfiles": [
{
"minApi": 28,
"maxApi": 30,
"baselineProfiles": [
"baselineProfiles/1/app-release.dm"
]
},
{
"minApi": 31,
"maxApi": 2147483647,
"baselineProfiles": [
"baselineProfiles/0/app-release.dm"
]
}
],
"minSdkVersionForDexing": 24
}
+7 -3
View File
@@ -7,7 +7,7 @@
android:required="true" /> android:required="true" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />-->
<uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
@@ -15,8 +15,8 @@
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" /> <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />-->
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.CAMERA"/>
@@ -31,6 +31,10 @@
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.RidgeScout" android:theme="@style/Theme.RidgeScout"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:networkSecurityConfig="@xml/network_security_config"
android:usesCleartextTraffic="true"
tools:targetApi="31"> tools:targetApi="31">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
@@ -1,9 +1,11 @@
package com.ridgebotics.ridgescout; package com.ridgebotics.ridgescout;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.view.MenuItem; import android.view.MenuItem;
import com.ridgebotics.ridgescout.databinding.ActivityMainBinding;
import com.ridgebotics.ridgescout.scoutingData.fields; import com.ridgebotics.ridgescout.scoutingData.fields;
import com.ridgebotics.ridgescout.utility.SentimentAnalysis; import com.ridgebotics.ridgescout.utility.SentimentAnalysis;
import com.ridgebotics.ridgescout.utility.AlertManager; import com.ridgebotics.ridgescout.utility.AlertManager;
@@ -18,12 +20,12 @@ import androidx.navigation.Navigation;
import androidx.navigation.ui.AppBarConfiguration; import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI; import androidx.navigation.ui.NavigationUI;
import com.ridgebotics.ridgescout.databinding.ActivityMainBinding;
import com.ridgebotics.ridgescout.SettingsVersionStack.latestSettings; import com.ridgebotics.ridgescout.utility.settingsManager;
import com.google.android.material.navigation.NavigationBarView; import com.google.android.material.navigation.NavigationBarView;
import java.util.Objects; import java.util.Objects;
import java.util.TimeZone;
public class MainActivity extends AppCompatActivity { public class MainActivity extends AppCompatActivity {
@@ -38,7 +40,9 @@ public class MainActivity extends AppCompatActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
latestSettings.update();
settingsManager.prefs = this.getSharedPreferences(
"com.ridgebotics.ridgescout", Context.MODE_PRIVATE);
if(!fileEditor.fileExist(fields.matchFieldsFilename)){ if(!fileEditor.fileExist(fields.matchFieldsFilename)){
fields.save(fields.matchFieldsFilename, fields.default_match_fields); fields.save(fields.matchFieldsFilename, fields.default_match_fields);
@@ -48,6 +52,9 @@ public class MainActivity extends AppCompatActivity {
fields.save(fields.pitsFieldsFilename, fields.default_pit_fields); fields.save(fields.pitsFieldsFilename, fields.default_pit_fields);
} }
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
AlertManager.init(this); AlertManager.init(this);
SentimentAnalysis.init(this); SentimentAnalysis.init(this);
@@ -66,30 +73,6 @@ public class MainActivity extends AppCompatActivity {
navView = findViewById(R.id.nav_view); 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( appBarConfiguration = new AppBarConfiguration.Builder(
R.id.navigation_scouting, R.id.navigation_scouting,
@@ -1,9 +0,0 @@
package com.ridgebotics.ridgescout.SettingsVersionStack;
public class latestSettings {
public static sv1 settings = new sv1();
public static void update(){
settings.init_settings();
settings.update();
}
}
@@ -1,110 +0,0 @@
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";
}
}
@@ -1,51 +0,0 @@
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");
}
}
@@ -1,84 +0,0 @@
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"));
}
}
@@ -3,6 +3,7 @@ package com.ridgebotics.ridgescout.scoutingData;
import com.ridgebotics.ridgescout.scoutingData.transfer.transferType; import com.ridgebotics.ridgescout.scoutingData.transfer.transferType;
import com.ridgebotics.ridgescout.types.ScoutingArray; import com.ridgebotics.ridgescout.types.ScoutingArray;
import com.ridgebotics.ridgescout.types.data.dataType; import com.ridgebotics.ridgescout.types.data.dataType;
import com.ridgebotics.ridgescout.types.data.intArrType;
import com.ridgebotics.ridgescout.types.data.stringType; import com.ridgebotics.ridgescout.types.data.stringType;
import com.ridgebotics.ridgescout.types.input.inputType; import com.ridgebotics.ridgescout.types.input.inputType;
import com.ridgebotics.ridgescout.types.data.intType; import com.ridgebotics.ridgescout.types.data.intType;
@@ -12,6 +13,7 @@ import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder; import com.ridgebotics.ridgescout.utility.ByteBuilder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
public class ScoutingDataWriter { public class ScoutingDataWriter {
// private static final int int_type_id = 255; // private static final int int_type_id = 255;
@@ -32,6 +34,9 @@ public class ScoutingDataWriter {
bb.addString((String) data[i].forceGetValue()); bb.addString((String) data[i].forceGetValue());
System.out.println("Saved STR: " + data[i].getName() + ", ("+ data[i].get() +")"); System.out.println("Saved STR: " + data[i].getName() + ", ("+ data[i].get() +")");
break; break;
case NUMARR:
bb.addIntArray((int[]) data[i].forceGetValue());
System.out.println("Saved INT Array: " + data[i].getName() + ", ("+ Arrays.toString((int[]) data[i].get()) +")");
} }
} }
byte[] bytes = bb.build(); byte[] bytes = bb.build();
@@ -73,6 +78,11 @@ public class ScoutingDataWriter {
dataTypes[i].forceSetValue(objects.get(i+2).get()); dataTypes[i].forceSetValue(objects.get(i+2).get());
System.out.println("Loaded STR: " + values[version][i].name + ", ("+ dataTypes[i].get() +")"); System.out.println("Loaded STR: " + values[version][i].name + ", ("+ dataTypes[i].get() +")");
break; break;
case 3: // Int array
dataTypes[i] = intArrType.newNull(values[version][i].name);
dataTypes[i].forceSetValue(objects.get(i+2).get());
System.out.println("Loaded intARR: " + values[version][i].name + ", ("+ Arrays.toString((int[])dataTypes[i].get()) +")");
break;
} }
} }
@@ -1,7 +1,10 @@
package com.ridgebotics.ridgescout.scoutingData; package com.ridgebotics.ridgescout.scoutingData;
import com.ridgebotics.ridgescout.types.input.checkboxType;
import com.ridgebotics.ridgescout.types.input.dropdownType; import com.ridgebotics.ridgescout.types.input.dropdownType;
import com.ridgebotics.ridgescout.types.input.fieldposType;
import com.ridgebotics.ridgescout.types.input.inputType; import com.ridgebotics.ridgescout.types.input.inputType;
import com.ridgebotics.ridgescout.types.input.numberType;
import com.ridgebotics.ridgescout.types.input.tallyType; import com.ridgebotics.ridgescout.types.input.tallyType;
import com.ridgebotics.ridgescout.types.input.textType; import com.ridgebotics.ridgescout.types.input.textType;
import com.ridgebotics.ridgescout.types.input.sliderType; import com.ridgebotics.ridgescout.types.input.sliderType;
@@ -20,6 +23,7 @@ public class fields {
public static final inputType[][] default_match_fields = new inputType[][] { public static final inputType[][] default_match_fields = new inputType[][] {
{ {
new fieldposType("Auto start pos", new int[]{0,0}),
new tallyType("Auto Notes", 0), new tallyType("Auto Notes", 0),
new sliderType("Auto Performance", 5, 0, 10), new sliderType("Auto Performance", 5, 0, 10),
new textType("Auto Comments", ""), new textType("Auto Comments", ""),
@@ -30,16 +34,13 @@ public class fields {
new textType("Overall Driving Comments", ""), new textType("Overall Driving Comments", ""),
new sliderType("Score area (AMP <-> Speaker)", 5, 0, 10), 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("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 dropdownType("Robot Condition", new String[]{"Everything was working", "Something was maybe broken", "Something was broken", "Robot was disabled for part of the match", "Missing robot (Joe Johnson)"}, 0),
new textType("Other Comments", "") new textType("Other Comments", "")
} }
}; };
public static final inputType[][] default_pit_fields = new inputType[][] { 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("How good is robot", 5, 0, 10),
new sliderType("Test", 1, 0, 10), new sliderType("Test", 1, 0, 10),
new textType("notes", ""), new textType("notes", ""),
@@ -113,6 +114,15 @@ public class fields {
case inputType.tallyType: case inputType.tallyType:
t = new tallyType(); t = new tallyType();
break; break;
case inputType.numberType:
t = new numberType();
break;
case inputType.checkboxType:
t = new checkboxType();
break;
case inputType.fieldposType:
t = new fieldposType();
break;
} }
t.decode((byte[]) obj.get()); t.decode((byte[]) obj.get());
@@ -3,7 +3,8 @@ package com.ridgebotics.ridgescout.types.data;
public abstract class dataType { public abstract class dataType {
public enum valueTypes { public enum valueTypes {
NUM, NUM,
STRING NUMARR,
STRING,
} }
private Object value; private Object value;
@@ -0,0 +1,54 @@
package com.ridgebotics.ridgescout.types.data;
public class intArrType extends dataType {
public static final int[] nullval = new int[]{255, 255};
// public static final int unselectedval = 1;
public valueTypes getValueType() {
return valueTypes.NUMARR;
}
// 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 intArrType(String name, int[] value) {
super(name);
set(value);
}
public static intArrType newNull(String name){
return new intArrType(name, nullval);
}
// 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());
// }
}
@@ -1,10 +1,17 @@
package com.ridgebotics.ridgescout.types; package com.ridgebotics.ridgescout.types;
import static com.ridgebotics.ridgescout.utility.DataManager.event;
import android.widget.TableRow;
import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import com.ridgebotics.ridgescout.utility.AlertManager; import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.BuiltByteParser; import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder; import com.ridgebotics.ridgescout.utility.ByteBuilder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;
public class frcEvent { public class frcEvent {
@@ -76,4 +83,62 @@ public class frcEvent {
matches.size() matches.size()
); );
} }
// A func that will return every match a team is in.
public frcMatch[] getTeamMatches(int teamNum){
List<frcMatch> teamMatches = new ArrayList<>();
for(int i = 0; i < matches.size(); i++){
frcMatch match = matches.get(i);
boolean isTeamMatch = false;
isTeamMatch = IntStream.of(match.redAlliance).anyMatch(x -> x == teamNum);
isTeamMatch = isTeamMatch || IntStream.of(match.blueAlliance).anyMatch(x -> x == teamNum);
if(isTeamMatch)
teamMatches.add(match);
}
return teamMatches.toArray(new frcMatch[0]);
}
// A func that will return the most recent match a team is in. (Not up until the current match)
public int getMostRecentTeamMatch(int teamNum, int curMatch){
frcMatch[] teamMatches = getTeamMatches(teamNum);
int maxMatch = - 1;
for(int i = 0; i < teamMatches.length; i++) {
if (teamMatches[i].matchIndex < curMatch &&
teamMatches[i].matchIndex > maxMatch) {
maxMatch = teamMatches[i].matchIndex;
}
}
if(maxMatch == -1)
return curMatch;
else
return maxMatch;
}
// public
// Returns the soonest match that there will be all the possible upcoming data on other teams
public void getReportMatches(int ourTeamNum){
frcMatch[] teamMatches = event.getTeamMatches(ourTeamNum);
for(int i = 0; i < teamMatches.length; i++){
int maxMatch = -1;
for(int a = 0; a < 6; a++){
int teamNum;
if(a < 3)
teamNum = teamMatches[i].redAlliance[a];
else
teamNum = teamMatches[i].blueAlliance[a-3];
if(teamNum == ourTeamNum)
continue;
int matchNum = event.getMostRecentTeamMatch(teamNum, teamMatches[i].matchIndex);
if(maxMatch < matchNum)
maxMatch = matchNum;
}
}
}
} }
@@ -60,4 +60,14 @@ public class frcMatch {
return "frcMatch Num: " + matchIndex + ", Blue: " + Arrays.toString(blueAlliance) + ", Red: " + Arrays.toString(redAlliance); return "frcMatch Num: " + matchIndex + ", Blue: " + Arrays.toString(blueAlliance) + ", Red: " + Arrays.toString(redAlliance);
} }
public String getTeamAlliance(int teamNum){
if(redAlliance[0] == teamNum) return "red-1";
if(redAlliance[1] == teamNum) return "red-2";
if(redAlliance[2] == teamNum) return "red-3";
if(blueAlliance[0] == teamNum) return "blue-1";
if(blueAlliance[1] == teamNum) return "blue-2";
if(blueAlliance[2] == teamNum) return "blue-3";
return "";
}
} }
@@ -0,0 +1,228 @@
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.CheckBox;
import android.widget.CompoundButton;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
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.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.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 checkboxType extends inputType {
public int get_byte_id() {return checkboxType;}
public inputTypes getInputType(){return inputTypes.CHECKBOX;}
public dataType.valueTypes getValueType(){return dataType.valueTypes.NUM;}
public Object get_fallback_value(){return 0;}
public checkboxType(){};
public String get_type_name(){return "Checkbox";}
public checkboxType(String name, int isChecked){
super(name);
this.default_value = isChecked;
}
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 PowerSpinnerView dropdown = null;
public CheckBox checkBox = null;
public View createView(Context context, Function<dataType, Integer> onUpdate){
checkBox = new CheckBox(context);
checkBox.setText(name);
checkBox.setTextSize(24);
setViewValue(default_value);
checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
onUpdate.apply(getViewValue());
}
});
return checkBox;
}
public void setViewValue(Object value) {
if(checkBox == null) return;
if(intType.isNull((int) value)){
nullify();
return;
}
isBlank = false;
checkBox.setVisibility(View.VISIBLE);
checkBox.setChecked((int) value == 1);
}
public void nullify(){
isBlank = true;
checkBox.setVisibility(View.GONE);
}
public dataType getViewValue(){
if(checkBox == null) return null;
if(checkBox.getVisibility() == View.GONE) return new intType(name, intType.nullval);
return new intType(name, checkBox.isChecked() ? 1 : 0);
}
public void add_individual_view(LinearLayout parent, dataType data){
if(data.isNull()) return;
CheckBox cb = new CheckBox(parent.getContext());
cb.setText(name);
cb.setChecked((int) data.get() == 1);
cb.setEnabled(false);
parent.addView(cb);
}
public static int[] colors = {0x7f00ff00, 0x7f7f0000};
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 numTrue = 0;
int numFalse = 0;
for(int i = 0; i < data.length; i++)
if(!data[i].isNull()){
if((int) data[i].get() == 1)
numTrue += 1;
else
numFalse += 1;
}
List<PieEntry> entries = new ArrayList<>();
entries.add(new PieEntry((float) numTrue, "True"));
entries.add(new PieEntry((float) numFalse, "False"));
PieDataSet pieDataSet = new PieDataSet(entries, name);
pieDataSet.setColors(colors);
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);
LineData lineData = new LineData();
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())
)
);
}
LineDataSet dataSet = new LineDataSet(entries, "is checked");
dataSet.setColor(Color.RED);
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,230 @@
package com.ridgebotics.ridgescout.types.input;
import static android.text.InputType.TYPE_CLASS_NUMBER;
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.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.components.Legend;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.data.LineDataSet;
import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.types.data.dataType;
import com.ridgebotics.ridgescout.types.data.intArrType;
import com.ridgebotics.ridgescout.types.data.intType;
import com.ridgebotics.ridgescout.ui.scouting.FieldPosView;
import com.ridgebotics.ridgescout.ui.scouting.MultiFieldPosView;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
public class fieldposType extends inputType {
public int get_byte_id() {return fieldposType;}
public inputTypes getInputType(){return inputTypes.FIELDPOS;}
public dataType.valueTypes getValueType(){return dataType.valueTypes.NUM;}
public Object get_fallback_value(){return 0;}
public fieldposType(){}
public String get_type_name(){return "Field Pos";}
public fieldposType(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.addIntArray((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();
System.out.println("Defalt value!!!!!" + default_value);
}
public FieldPosView field = null;
public View createView(Context context, Function<dataType, Integer> onUpdate){
field = new FieldPosView(context, pos -> {
onUpdate.apply(new intArrType(name, pos));
});
setViewValue(default_value);
return field;
}
public void setViewValue(Object value) {
if(field == null) return;
if(intArrType.isNull((int[]) value)){
nullify();
return;
}
isBlank = false;
field.setVisibility(View.VISIBLE);
field.setPos((int[]) value);
}
public void nullify(){
isBlank = true;
field.setVisibility(View.GONE);
}
public dataType getViewValue(){
if(field == null) return null;
if(field.getVisibility() == View.GONE) return intArrType.newNull(name);
return new intArrType(name, field.getPos());
}
public void add_individual_view(LinearLayout parent, dataType data){
if(data.isNull()) return;
FieldPosView fp = new FieldPosView(parent.getContext());
fp.setEnabled(false);
fp.setPos((int[]) data.get());
parent.addView(fp);
}
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){
MultiFieldPosView mfp = new MultiFieldPosView(parent.getContext());
for(int i = 0; i < data.length; i++){
if(data[i].isNull()) continue;
mfp.addPos((int[]) data[i].get());
}
parent.addView(mfp);
}
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 = 0;
int max = 255;
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, 255-(float)((int[]) data[i].get())[1]));
}
LineDataSet dataSet = new LineDataSet(entries, "Field position Y value");
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);
}
}
@@ -15,11 +15,18 @@ public abstract class inputType {
public static final int dropdownType = 254; public static final int dropdownType = 254;
public static final int notesType = 253; public static final int notesType = 253;
public static final int tallyType = 252; public static final int tallyType = 252;
public static final int numberType = 251;
public static final int checkboxType = 250;
public static final int fieldposType = 249;
public enum inputTypes { public enum inputTypes {
SLIDER, SLIDER,
DROPDOWN, DROPDOWN,
NOTES_INPUT, NOTES_INPUT,
TALLY TALLY,
NUMBER,
CHECKBOX,
FIELDPOS
} }
public String name; public String name;
public Object default_value; public Object default_value;
@@ -0,0 +1,319 @@
package com.ridgebotics.ridgescout.types.input;
import static android.text.InputType.TYPE_CLASS_NUMBER;
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.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.components.Legend;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.data.LineDataSet;
import com.ridgebotics.ridgescout.types.data.dataType;
import com.ridgebotics.ridgescout.types.data.intType;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
public class numberType extends inputType {
public int get_byte_id() {return numberType;}
public inputTypes getInputType(){return inputTypes.NUMBER;}
public dataType.valueTypes getValueType(){return dataType.valueTypes.NUM;}
public Object get_fallback_value(){return 0;}
public numberType(){}
public String get_type_name(){return "Number";}
public numberType(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 EditText num = null;
public View createView(Context context, Function<dataType, Integer> onUpdate){
num = new EditText(context);
num.setInputType(TYPE_CLASS_NUMBER);
num.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) {}
});
setViewValue(default_value);
return num;
}
public void setViewValue(Object value) {
if(num == null) return;
if(intType.isNull((int)value)){
nullify();
return;
}
isBlank = false;
num.setVisibility(View.VISIBLE);
num.setText(String.valueOf(value));
}
public void nullify(){
isBlank = true;
num.setVisibility(View.GONE);
}
public dataType getViewValue(){
if(num == null) return null;
if(num.getVisibility() == View.GONE) return intType.newNull(name);
return new intType(name, safeToInt(num.getText().toString()));
}
private int safeToInt(String num){
if(num.isEmpty())
return intType.nullval;
try {
return Integer.parseInt(num);
}catch (NumberFormatException e){
return intType.nullval;
}
}
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);
}
}
@@ -1,8 +1,5 @@
package com.ridgebotics.ridgescout.ui.data; package com.ridgebotics.ridgescout.ui.data;
import static com.ridgebotics.ridgescout.SettingsVersionStack.latestSettings.settings;
import static com.ridgebotics.ridgescout.utility.DataManager.event;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@@ -13,14 +10,6 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.databinding.FragmentDataCompareBinding; import com.ridgebotics.ridgescout.databinding.FragmentDataCompareBinding;
import com.ridgebotics.ridgescout.databinding.FragmentDataReportBinding;
import com.ridgebotics.ridgescout.types.frcMatch;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.DataManager;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;
public class CompareFragment extends Fragment { public class CompareFragment extends Fragment {
FragmentDataCompareBinding binding; FragmentDataCompareBinding binding;
@@ -14,7 +14,7 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.R; import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.SettingsVersionStack.latestSettings; import com.ridgebotics.ridgescout.utility.settingsManager;
import com.ridgebotics.ridgescout.databinding.FragmentDataBinding; import com.ridgebotics.ridgescout.databinding.FragmentDataBinding;
import com.ridgebotics.ridgescout.types.frcTeam; import com.ridgebotics.ridgescout.types.frcTeam;
import com.ridgebotics.ridgescout.ui.TeamSelectorFragment; import com.ridgebotics.ridgescout.ui.TeamSelectorFragment;
@@ -33,7 +33,7 @@ public class DataFragment extends Fragment {
binding = FragmentDataBinding.inflate(inflater, container, false); binding = FragmentDataBinding.inflate(inflater, container, false);
View root = binding.getRoot(); View root = binding.getRoot();
String evcode = latestSettings.settings.get_evcode(); String evcode = settingsManager.getEVCode();
binding.fieldsButton.setOnClickListener(v -> { binding.fieldsButton.setOnClickListener(v -> {
findNavController(this).navigate(R.id.action_navigation_data_to_navigation_data_fields_chooser); findNavController(this).navigate(R.id.action_navigation_data_to_navigation_data_fields_chooser);
@@ -71,7 +71,7 @@ public class DataFragment extends Fragment {
}); });
binding.reportButton.setOnClickListener(v -> { binding.reportButton.setOnClickListener(v -> {
findNavController(this).navigate(R.id.action_navigation_data_to_navigation_data_report); findNavController(this).navigate(R.id.action_navigation_data_to_navigation_data_report_selector);
}); });
return root; return root;
@@ -8,8 +8,11 @@ import android.widget.EditText;
import android.widget.TableLayout; import android.widget.TableLayout;
import android.widget.TextView; import android.widget.TextView;
import com.ridgebotics.ridgescout.types.input.checkboxType;
import com.ridgebotics.ridgescout.types.input.dropdownType; import com.ridgebotics.ridgescout.types.input.dropdownType;
import com.ridgebotics.ridgescout.types.input.fieldposType;
import com.ridgebotics.ridgescout.types.input.inputType; import com.ridgebotics.ridgescout.types.input.inputType;
import com.ridgebotics.ridgescout.types.input.numberType;
import com.ridgebotics.ridgescout.types.input.sliderType; import com.ridgebotics.ridgescout.types.input.sliderType;
import com.ridgebotics.ridgescout.types.input.tallyType; import com.ridgebotics.ridgescout.types.input.tallyType;
import com.ridgebotics.ridgescout.types.input.textType; import com.ridgebotics.ridgescout.types.input.textType;
@@ -19,7 +22,8 @@ public class FieldEditorHelper {
private enum parameterTypeEnum { private enum parameterTypeEnum {
paramNumber, paramNumber,
paramString, paramString,
paramStringArray paramStringArray,
paramNumberArray
} }
public static class parameterType { public static class parameterType {
@@ -54,6 +58,15 @@ public class FieldEditorHelper {
} }
} }
// public static class paramNumberArray extends parameterType{
// public int[] val;
// public paramNumberArray(String name, int[] val){
// this.name = name + " (Number array)";
// this.val = val;
// this.id = parameterTypeEnum.paramNumberArray;
// }
// }
public static final parameterType[] defaultSliderParams = new parameterType[]{ public static final parameterType[] defaultSliderParams = new parameterType[]{
new paramNumber("Min", 0), new paramNumber("Min", 0),
new paramNumber("Max", 10), new paramNumber("Max", 10),
@@ -69,6 +82,16 @@ public class FieldEditorHelper {
public static final parameterType[] defaultTallyParams = new parameterType[]{ public static final parameterType[] defaultTallyParams = new parameterType[]{
new paramNumber("Default Value", 0) new paramNumber("Default Value", 0)
}; };
public static final parameterType[] defaultNumberParams = new parameterType[]{
new paramNumber("Default Value", 0)
};
public static final parameterType[] defaultCheckboxParam = new parameterType[]{
new paramNumber("Default Value ( 1 or 0 )", 0)
};
public static final parameterType[] defaultFieldPosParam = new parameterType[]{
new paramNumber("Default X", 0),
new paramNumber("Default Y", 0)
};
private static parameterType[] getSliderParams(sliderType s){ private static parameterType[] getSliderParams(sliderType s){
@@ -98,6 +121,25 @@ public class FieldEditorHelper {
}; };
} }
private static parameterType[] getNumberParams(numberType s){
return new parameterType[]{
new paramNumber("Default Value", (int) s.default_value)
};
}
private static parameterType[] getCheckboxParam(checkboxType s){
return new parameterType[]{
new paramNumber("Default Value ( 1 or 0 )", (int) s.default_value)
};
}
private static parameterType[] getFieldPosParam(fieldposType s){
return new parameterType[]{
new paramNumber("Default X", ((int[]) s.default_value)[0]),
new paramNumber("Default Y", ((int[]) s.default_value)[1])
};
}
public static void setSliderParams(sliderType s, parameterType[] types){ public static void setSliderParams(sliderType s, parameterType[] types){
@@ -119,6 +161,22 @@ public class FieldEditorHelper {
s.default_value = ((paramNumber) types[0]).val; s.default_value = ((paramNumber) types[0]).val;
} }
public static void setNumberParams(numberType s, parameterType[] types){
s.default_value = ((paramNumber) types[0]).val;
}
public static void setCheckboxParam(checkboxType s, parameterType[] types){
s.default_value = ((paramNumber) types[0]).val;
}
public static void setFieldPosParam(fieldposType s, parameterType[] types){
s.default_value = new int[]{
((paramNumber) types[0]).val,
((paramNumber) types[1]).val
};
}
private static void setInputParameter(inputType t, parameterType[] types){ private static void setInputParameter(inputType t, parameterType[] types){
switch (t.getInputType()){ switch (t.getInputType()){
case TALLY: case TALLY:
@@ -133,6 +191,15 @@ public class FieldEditorHelper {
case NOTES_INPUT: case NOTES_INPUT:
setTextParams((textType) t, types); setTextParams((textType) t, types);
break; break;
case NUMBER:
setNumberParams((numberType) t, types);
break;
case CHECKBOX:
setCheckboxParam((checkboxType) t, types);
break;
case FIELDPOS:
setFieldPosParam((fieldposType) t, types);
break;
} }
} }
@@ -148,6 +215,12 @@ public class FieldEditorHelper {
return getDropdownParams((dropdownType) t); return getDropdownParams((dropdownType) t);
case NOTES_INPUT: case NOTES_INPUT:
return getTextParams((textType) t); return getTextParams((textType) t);
case NUMBER:
return getNumberParams((numberType) t);
case CHECKBOX:
return getCheckboxParam((checkboxType) t);
case FIELDPOS:
return getFieldPosParam((fieldposType) t);
} }
return new parameterType[]{}; return new parameterType[]{};
} }
@@ -24,8 +24,11 @@ import androidx.navigation.Navigation;
import com.ridgebotics.ridgescout.R; import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.databinding.FragmentDataFieldsBinding; import com.ridgebotics.ridgescout.databinding.FragmentDataFieldsBinding;
import com.ridgebotics.ridgescout.scoutingData.fields; import com.ridgebotics.ridgescout.scoutingData.fields;
import com.ridgebotics.ridgescout.types.input.checkboxType;
import com.ridgebotics.ridgescout.types.input.dropdownType; import com.ridgebotics.ridgescout.types.input.dropdownType;
import com.ridgebotics.ridgescout.types.input.fieldposType;
import com.ridgebotics.ridgescout.types.input.inputType; import com.ridgebotics.ridgescout.types.input.inputType;
import com.ridgebotics.ridgescout.types.input.numberType;
import com.ridgebotics.ridgescout.types.input.sliderType; import com.ridgebotics.ridgescout.types.input.sliderType;
import com.ridgebotics.ridgescout.types.input.tallyType; import com.ridgebotics.ridgescout.types.input.tallyType;
import com.ridgebotics.ridgescout.types.input.textType; import com.ridgebotics.ridgescout.types.input.textType;
@@ -52,10 +55,8 @@ public class FieldsFragment extends Fragment {
@Nullable Bundle savedInstanceState) { @Nullable Bundle savedInstanceState) {
binding = FragmentDataFieldsBinding.inflate(inflater, container, false); binding = FragmentDataFieldsBinding.inflate(inflater, container, false);
binding.revertVersionButton.setVisibility(View.VISIBLE);
binding.valueEditScrollview.setOnTouchListener((v, event) -> true); binding.valueEditScrollview.setOnTouchListener((v, event) -> true);
binding.saveButton.setVisibility(View.GONE); binding.saveButton.setVisibility(View.GONE);
binding.cancelEditButton.setVisibility(View.GONE); binding.cancelEditButton.setVisibility(View.GONE);
binding.editButton.setVisibility(View.GONE); binding.editButton.setVisibility(View.GONE);
@@ -121,6 +122,25 @@ public class FieldsFragment extends Fragment {
tr.setBackgroundColor(unfocused_background_color); tr.setBackgroundColor(unfocused_background_color);
} }
} }
if(values.length > 1) {
binding.revertVersionButton.setVisibility(View.VISIBLE);
binding.revertVersionButton.setOnClickListener(v -> {
AlertDialog.Builder alert = new AlertDialog.Builder(getContext());
alert.setTitle("Warning!");
alert.setMessage("If there is any data set this version, it will be deleted!");
alert.setPositiveButton("OK", (dialog, which) -> {
inputType[][] newArr = new inputType[values.length - 1][];
System.arraycopy(values, 0, newArr, 0, values.length - 1);
if(fields.save(filename, newArr))
AlertManager.toast("Saved");
load_field_menu();
});
alert.setNegativeButton("Cancel", null);
alert.setCancelable(true);
alert.create().show();
});
}
} }
private void display_fields(inputType[] version_values) { private void display_fields(inputType[] version_values) {
@@ -179,10 +199,7 @@ public class FieldsFragment extends Fragment {
AlertDialog.Builder alert = new AlertDialog.Builder(getContext()); AlertDialog.Builder alert = new AlertDialog.Builder(getContext());
alert.setTitle("Warning!"); 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.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.setPositiveButton("OK", (dialog, which) -> {
alert.setNegativeButton("Cancel", null);
alert.setCancelable(true);
alert.setOnDismissListener(b -> {
inputType[][] currentValues = fields.load(filename); inputType[][] currentValues = fields.load(filename);
assert currentValues != null; assert currentValues != null;
inputType[][] newValues = new inputType[currentValues.length+1][]; inputType[][] newValues = new inputType[currentValues.length+1][];
@@ -195,11 +212,13 @@ public class FieldsFragment extends Fragment {
} }
// newValues[newValues.length-1] = values[values.length-1]; // newValues[newValues.length-1] = values[values.length-1];
boolean saved = fields.save(filename, newValues); if(fields.save(filename, newValues))
AlertManager.alert("Saved", String.valueOf(saved)); AlertManager.toast("Saved");
Navigation.findNavController((Activity) getContext(), R.id.nav_host_fragment_activity_main).navigate(R.id.action_navigation_data_fields_to_navigation_data_fields_chooser); Navigation.findNavController((Activity) getContext(), R.id.nav_host_fragment_activity_main).navigate(R.id.action_navigation_data_fields_to_navigation_data_fields_chooser);
}); });
alert.setNegativeButton("Cancel", null);
alert.setCancelable(true);
alert.create().show(); alert.create().show();
} }
@@ -381,6 +400,9 @@ public class FieldsFragment extends Fragment {
iconSpinnerItems.add(new IconSpinnerItem("Text")); iconSpinnerItems.add(new IconSpinnerItem("Text"));
iconSpinnerItems.add(new IconSpinnerItem("Dropdown")); iconSpinnerItems.add(new IconSpinnerItem("Dropdown"));
iconSpinnerItems.add(new IconSpinnerItem("Tally")); iconSpinnerItems.add(new IconSpinnerItem("Tally"));
iconSpinnerItems.add(new IconSpinnerItem("Number"));
iconSpinnerItems.add(new IconSpinnerItem("Checkbox"));
iconSpinnerItems.add(new IconSpinnerItem("Field Position"));
IconSpinnerAdapter iconSpinnerAdapter = new IconSpinnerAdapter(dropdown); IconSpinnerAdapter iconSpinnerAdapter = new IconSpinnerAdapter(dropdown);
@@ -445,6 +467,24 @@ public class FieldsFragment extends Fragment {
FieldEditorHelper.setTallyParams(tally, FieldEditorHelper.defaultTallyParams); FieldEditorHelper.setTallyParams(tally, FieldEditorHelper.defaultTallyParams);
addField_Part_4(tally); addField_Part_4(tally);
break; break;
case 4:
numberType num = new numberType();
num.name = title;
FieldEditorHelper.setNumberParams(num, FieldEditorHelper.defaultNumberParams);
addField_Part_4(num);
break;
case 5:
checkboxType cb = new checkboxType();
cb.name = title;
FieldEditorHelper.setCheckboxParam(cb, FieldEditorHelper.defaultCheckboxParam);
addField_Part_4(cb);
break;
case 6:
fieldposType fp = new fieldposType();
fp.name = title;
FieldEditorHelper.setFieldPosParam(fp, FieldEditorHelper.defaultFieldPosParam);
addField_Part_4(fp);
break;
} }
@@ -479,7 +519,7 @@ public class FieldsFragment extends Fragment {
newValues[newValues.length-1] = field; newValues[newValues.length-1] = field;
values[values.length-1] = newValues; values[values.length-1] = newValues;
AlertManager.alert("Test", String.valueOf(binding.fieldsArea.getReorderedIndexes())); // AlertManager.alert("Test", String.valueOf(binding.fieldsArea.getReorderedIndexes()));
//TableRow tr = getTableRow(field); //TableRow tr = getTableRow(field);
//binding.fieldsArea.addView(tr); //binding.fieldsArea.addView(tr);
@@ -1,6 +1,5 @@
package com.ridgebotics.ridgescout.ui.data; package com.ridgebotics.ridgescout.ui.data;
import static com.ridgebotics.ridgescout.SettingsVersionStack.latestSettings.settings;
import static com.ridgebotics.ridgescout.utility.DataManager.event; import static com.ridgebotics.ridgescout.utility.DataManager.event;
import android.os.Bundle; import android.os.Bundle;
@@ -16,106 +15,75 @@ import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.databinding.FragmentDataReportBinding; import com.ridgebotics.ridgescout.databinding.FragmentDataReportBinding;
import com.ridgebotics.ridgescout.types.frcMatch; import com.ridgebotics.ridgescout.types.frcMatch;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.DataManager; import com.ridgebotics.ridgescout.utility.DataManager;
import com.ridgebotics.ridgescout.utility.ollama.OllamaClient;
import java.util.ArrayList; import com.ridgebotics.ridgescout.utility.ollama.PromptCreator;
import java.util.List; import com.ridgebotics.ridgescout.utility.settingsManager;
import java.util.stream.IntStream;
public class ReportFragment extends Fragment { public class ReportFragment extends Fragment {
FragmentDataReportBinding binding; FragmentDataReportBinding binding;
private static frcMatch match;
public static void setMatch(frcMatch m){
match = m;
}
private final int ourTeamNum = settingsManager.getTeamNum();
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) { @Nullable Bundle savedInstanceState) {
binding = FragmentDataReportBinding.inflate(inflater, container, false); binding = FragmentDataReportBinding.inflate(inflater, container, false);
getReportMatches(); binding.teamNumber.setText(String.valueOf(ourTeamNum));
binding.AyEyeBox.setVisibility(View.VISIBLE);
binding.AyEyeButton.setVisibility(View.GONE);
DataManager.reload_event();
DataManager.reload_pit_fields();
DataManager.reload_match_fields();
binding.AyEyeBox.setText("TBD!\n This is meant to be a tool that lets scouters more easily write reports to the drive team before matches. There are some plans for LLM integration into this menu ");
// binding.AyEyeButton.setText("Create Prompt");
// binding.AyEyeButton.setOnClickListener(a ->{
// getPrompt();
// binding.AyEyeButton.setText("Generate Overview");
// binding.AyEyeButton.setOnClickListener(b ->{
// AIDataOverview();
// binding.AyEyeButton.setVisibility(View.GONE);
// });
// });
return binding.getRoot(); return binding.getRoot();
} }
public frcMatch[] getTeamMatches(int teamNum){ private void getPrompt(){
DataManager.reload_event(); binding.AyEyeBox.setVisibility(View.VISIBLE);
List<frcMatch> teamMatches = new ArrayList<>(); String prompt = PromptCreator.genMatchPrompt(0);
for(int i = 0; i < event.matches.size(); i++){ binding.AyEyeBox.setText(prompt);
frcMatch match = event.matches.get(i);
boolean isTeamMatch = false;
isTeamMatch = IntStream.of(match.redAlliance).anyMatch(x -> x == teamNum);
isTeamMatch = isTeamMatch || IntStream.of(match.blueAlliance).anyMatch(x -> x == teamNum);
if(isTeamMatch)
teamMatches.add(match);
}
return teamMatches.toArray(new frcMatch[0]);
} }
private int getMostRecentTeamMatch(int teamNum, int curMatch){ private void AIDataOverview(){
frcMatch[] teamMatches = getTeamMatches(teamNum); String prompt = binding.AyEyeBox.getText().toString();
int maxMatch = - 1; binding.AyEyeBox.setText("");
OllamaClient.run(prompt, new OllamaClient.ollamaListener() {
for(int i = 0; i < teamMatches.length; i++) { @Override
if (teamMatches[i].matchIndex < curMatch && public void onResponse(String response) {
teamMatches[i].matchIndex > maxMatch) { // System.out.println(response);
maxMatch = teamMatches[i].matchIndex; getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
binding.AyEyeBox.setText(binding.AyEyeBox.getText()+response);
}
});
} }
} @Override
public void onComplete() {
if(maxMatch == -1) System.out.println(binding.AyEyeBox.getText());
return curMatch;
else
return maxMatch;
}
public void getReportMatches(){
// String out = "";
int ourTeamNum = settings.get_team_num();
frcMatch[] teamMatches = getTeamMatches(ourTeamNum);
TableRow tr = new TableRow(getContext());
TextView tv = new TextView(getContext());
tv.setText("Team match");
tr.addView(tv);
tv = new TextView(getContext());
tv.setText("Most informed match");
tr.addView(tv);
binding.teamMatchesTable.addView(tr);
binding.teamMatchesTable.setStretchAllColumns(true);
for(int i = 0; i < teamMatches.length; i++){
tr = new TableRow(getContext());
tv = new TextView(getContext());
tv.setText(String.valueOf(teamMatches[i].matchIndex));
tr.addView(tv);
int maxMatch = -1;
for(int a = 0; a < 6; a++){
int teamNum = 0;
if(a < 3)
teamNum = teamMatches[i].redAlliance[a];
else
teamNum = teamMatches[i].blueAlliance[a-3];
if(teamNum == ourTeamNum)
continue;
int matchNum = getMostRecentTeamMatch(teamNum, teamMatches[i].matchIndex);
if(maxMatch < matchNum)
maxMatch = matchNum;
} }
});
tv = new TextView(getContext());
tv.setText(String.valueOf(maxMatch));
tr.addView(tv);
binding.teamMatchesTable.addView(tr);
}
// AlertManager.error(out);
} }
} }
@@ -0,0 +1,82 @@
package com.ridgebotics.ridgescout.ui.data;
import static androidx.navigation.fragment.FragmentKt.findNavController;
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.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.R;
import com.ridgebotics.ridgescout.databinding.FragmentDataReportSelectorBinding;
import com.ridgebotics.ridgescout.types.frcMatch;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.DataManager;
import com.ridgebotics.ridgescout.utility.settingsManager;
public class ReportSelectorFragment extends Fragment {
FragmentDataReportSelectorBinding binding;
private final int teamNum = settingsManager.getTeamNum();
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentDataReportSelectorBinding.inflate(inflater, container, false);
binding.matchTable.setStretchAllColumns(true);
DataManager.reload_event();
frcMatch[] teamMatches = event.getTeamMatches(teamNum);
if(teamMatches.length == 0){
AlertManager.error("Team number " + teamNum + " could not be found in event " + evcode);
findNavController(this).navigate(R.id.action_navigation_data_report_selector_to_navigation_data);
}
for(int i = 0; i < teamMatches.length; i++){
addTableRow(teamMatches[i]);
}
return binding.getRoot();
}
@SuppressLint("SetTextI18n")
private void addTableRow(frcMatch match){
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(0x5000ff00);
binding.matchTable.addView(tr);
TextView tv = new TextView(getContext());
tv.setText("Match " + match.matchIndex);
tv.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
tr.addView(tv);
tv = new TextView(getContext());
tv.setText("Pos " + match.getTeamAlliance(teamNum));
tv.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
tr.addView(tv);
tr.setOnClickListener(v -> {
ReportFragment.setMatch(match);
findNavController(this).navigate(R.id.action_navigation_data_report_selector_to_navigation_data_report);
});
}
}
@@ -22,7 +22,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.SettingsVersionStack.latestSettings; import com.ridgebotics.ridgescout.utility.settingsManager;
import com.ridgebotics.ridgescout.databinding.FragmentDataTeamsBinding; import com.ridgebotics.ridgescout.databinding.FragmentDataTeamsBinding;
import com.ridgebotics.ridgescout.scoutingData.ScoutingDataWriter; import com.ridgebotics.ridgescout.scoutingData.ScoutingDataWriter;
import com.ridgebotics.ridgescout.types.data.dataType; import com.ridgebotics.ridgescout.types.data.dataType;
@@ -64,7 +64,7 @@ public class TeamsFragment extends Fragment {
table.setStretchAllColumns(true); table.setStretchAllColumns(true);
binding.teamsArea.addView(table); binding.teamsArea.addView(table);
loadTeam(latestSettings.settings.get_data_view_mode()); loadTeam(settingsManager.getDataMode());
return binding.getRoot(); return binding.getRoot();
} }
@@ -114,7 +114,7 @@ public class TeamsFragment extends Fragment {
public void onItemSelected(int oldIndex, @Nullable IconSpinnerItem oldItem, int newIndex, public void onItemSelected(int oldIndex, @Nullable IconSpinnerItem oldItem, int newIndex,
IconSpinnerItem newItem) { IconSpinnerItem newItem) {
latestSettings.settings.set_data_view_mode(newIndex); settingsManager.setDataMode(newIndex);
loadTeam(newIndex); loadTeam(newIndex);
} }
}); });
@@ -0,0 +1,115 @@
package com.ridgebotics.ridgescout.ui.scouting;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.FrameLayout;
import android.widget.ImageView;
import com.ridgebotics.ridgescout.R;
public class FieldPosView extends FrameLayout {
private int x = -1;
private int y = -1;
private Paint paint;
private static final float POINT_RADIUS = 10f;
private ImageView imageView;
private boolean enabled = true;
public interface onTapListener {
void onUpdate(int[] pos);
};
public FieldPosView(Context context) {
super(context);
init(context, pos -> {});
}
public FieldPosView(Context context, onTapListener tapListener) {
super(context);
init(context, tapListener);
}
public FieldPosView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, pos -> {});
}
@SuppressLint("ClickableViewAccessibility")
private void init(Context context, onTapListener tl) {
// Initialize paint
paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
// Create and add ImageView
imageView = new ImageView(context);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT
);
imageView.setLayoutParams(params);
imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
imageView.setAdjustViewBounds(true);
addView(imageView);
// Set touch listener
setOnTouchListener((v, event) -> {
if (enabled && event.getAction() == MotionEvent.ACTION_DOWN) {
x = (int) ((event.getX()/getWidth())*255);
y = (int) ((event.getY()/getHeight())*255);
tl.onUpdate(getPos());
invalidate();
return true;
}
return false;
});
setImageResource(R.drawable.field_2024);
}
public void setPos(int[] pos){
if(pos.length == 0) return;
x = pos[0];
y = pos[1];
invalidate();
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (x >= 0 && y >= 0) {
canvas.drawCircle(
((float) x /255)*getWidth(),
((float) y /255)*getHeight(),
POINT_RADIUS, paint);
}
}
public void setImageResource(int resId) {
imageView.setImageResource(resId);
}
public void setImageDrawable(Drawable drawable) {
imageView.setImageDrawable(drawable);
}
public void setImageBitmap(Bitmap bitmap) {
imageView.setImageBitmap(bitmap);
}
public int[] getPos() {
return new int[]{
x,
y
};
}
}
@@ -14,7 +14,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.SettingsVersionStack.latestSettings; import com.ridgebotics.ridgescout.utility.settingsManager;
import com.ridgebotics.ridgescout.databinding.FragmentScoutingMatchBinding; import com.ridgebotics.ridgescout.databinding.FragmentScoutingMatchBinding;
import com.ridgebotics.ridgescout.scoutingData.ScoutingDataWriter; import com.ridgebotics.ridgescout.scoutingData.ScoutingDataWriter;
import com.ridgebotics.ridgescout.types.data.dataType; import com.ridgebotics.ridgescout.types.data.dataType;
@@ -41,8 +41,8 @@ public class MatchScoutingFragment extends Fragment {
DataManager.reload_match_fields(); DataManager.reload_match_fields();
alliance_position = latestSettings.settings.get_alliance_pos(); alliance_position = settingsManager.getAllyPos();
username = latestSettings.settings.get_username(); username = settingsManager.getUsername();
binding.username.setText(username); binding.username.setText(username);
binding.alliancePosText.setText(alliance_position); binding.alliancePosText.setText(alliance_position);
@@ -64,12 +64,12 @@ public class MatchScoutingFragment extends Fragment {
cur_match_num = latestSettings.settings.get_match_num(); cur_match_num = settingsManager.getMatchNum();
update_match_num(); update_match_num();
binding.nextButton.setOnClickListener(v -> { binding.nextButton.setOnClickListener(v -> {
if(edited) save(); if(edited) save();
latestSettings.settings.set_match_num(cur_match_num+1); settingsManager.setMatchNum(cur_match_num+1);
cur_match_num += 1; cur_match_num += 1;
update_match_num(); update_match_num();
update_scouting_data(); update_scouting_data();
@@ -83,7 +83,7 @@ public class MatchScoutingFragment extends Fragment {
if(edited) save(); if(edited) save();
alliance_position = incrementMatchPos(alliance_position); alliance_position = incrementMatchPos(alliance_position);
latestSettings.settings.set_alliance_pos(alliance_position); settingsManager.setAllyPos(alliance_position);
binding.alliancePosText.setText(alliance_position); binding.alliancePosText.setText(alliance_position);
update_match_num(); update_match_num();
@@ -93,7 +93,7 @@ public class MatchScoutingFragment extends Fragment {
binding.backButton.setOnClickListener(v -> { binding.backButton.setOnClickListener(v -> {
if(edited) save(); if(edited) save();
latestSettings.settings.set_match_num(cur_match_num-1); settingsManager.setMatchNum(cur_match_num-1);
cur_match_num -= 1; cur_match_num -= 1;
update_match_num(); update_match_num();
update_scouting_data(); update_scouting_data();
@@ -310,8 +310,14 @@ public class MatchScoutingFragment extends Fragment {
default_fields(); default_fields();
set_indicator_color(unsaved_color); set_indicator_color(unsaved_color);
}else{ }else{
get_fields(); try {
set_indicator_color(saved_color); get_fields();
set_indicator_color(saved_color);
} catch (Exception e){
AlertManager.error(e);
default_fields();
set_indicator_color(unsaved_color);
}
} }
asm.start(); asm.start();
@@ -0,0 +1,86 @@
package com.ridgebotics.ridgescout.ui.scouting;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.FrameLayout;
import android.widget.ImageView;
import com.ridgebotics.ridgescout.R;
import java.util.ArrayList;
import java.util.List;
public class MultiFieldPosView extends FrameLayout {
private Paint paint;
private static final float POINT_RADIUS = 10f;
private ImageView imageView;
private List<Integer[]> points = new ArrayList<>();
public MultiFieldPosView(Context context) {
super(context);
init(context);
}
public MultiFieldPosView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
@SuppressLint("ClickableViewAccessibility")
private void init(Context context) {
// Initialize paint
paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
// Create and add ImageView
imageView = new ImageView(context);
LayoutParams params = new LayoutParams(
LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT
);
imageView.setLayoutParams(params);
imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
imageView.setAdjustViewBounds(true);
addView(imageView);
setImageResource(R.drawable.field_2024);
}
public void addPos(int[] pos){
if(pos.length != 2) return;
points.add(new Integer[]{
pos[0],
pos[1]
});
invalidate();
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
for(int i = 0; i < points.size(); i++){
int x = points.get(i)[0];
int y = points.get(i)[1];
if (x >= 0 && y >= 0) {
canvas.drawCircle(
((float) x / 255) * getWidth(),
((float) y / 255) * getHeight(),
POINT_RADIUS, paint);
}
}
}
public void setImageResource(int resId) {
imageView.setImageResource(resId);
}
}
@@ -15,7 +15,8 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.SettingsVersionStack.latestSettings; import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.settingsManager;
import com.ridgebotics.ridgescout.databinding.FragmentScoutingPitBinding; import com.ridgebotics.ridgescout.databinding.FragmentScoutingPitBinding;
import com.ridgebotics.ridgescout.scoutingData.ScoutingDataWriter; import com.ridgebotics.ridgescout.scoutingData.ScoutingDataWriter;
import com.ridgebotics.ridgescout.types.data.dataType; import com.ridgebotics.ridgescout.types.data.dataType;
@@ -43,7 +44,7 @@ public class PitScoutingFragment extends Fragment {
binding = FragmentScoutingPitBinding.inflate(inflater, container, false); binding = FragmentScoutingPitBinding.inflate(inflater, container, false);
username = latestSettings.settings.get_username(); username = settingsManager.getUsername();
DataManager.reload_pit_fields(); DataManager.reload_pit_fields();
loadTeam(); loadTeam();
@@ -74,9 +75,10 @@ public class PitScoutingFragment extends Fragment {
types[i] = pit_latest_values[i].getViewValue(); types[i] = pit_latest_values[i].getViewValue();
} }
if(ScoutingDataWriter.save(pit_values.length-1, username, filename, types)) if(ScoutingDataWriter.save(pit_values.length-1, username, filename, types)) {
System.out.println("Saved!"); System.out.println("Saved!");
else AlertManager.toast("Saved " + filename);
}else
System.out.println("Error saving"); System.out.println("Error saving");
} }
@@ -92,50 +94,6 @@ public class PitScoutingFragment extends Fragment {
} }
// 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(){ public void loadTeam(){
// clear_fields(); // clear_fields();
@@ -147,9 +105,6 @@ public class PitScoutingFragment extends Fragment {
binding.pitTeamDescription.setText(team.getDescription()); binding.pitTeamDescription.setText(team.getDescription());
binding.pitBarTeamNum.setText(String.valueOf(team.teamNumber)); binding.pitBarTeamNum.setText(String.valueOf(team.teamNumber));
// binding.teamName.setText(team.teamName);
// binding.teamDescription.setText(team.getDescription());
filename = evcode + "-" + team.teamNumber + ".pitscoutdata"; filename = evcode + "-" + team.teamNumber + ".pitscoutdata";
boolean new_file = !fileEditor.fileExist(filename); boolean new_file = !fileEditor.fileExist(filename);
@@ -164,8 +119,14 @@ public class PitScoutingFragment extends Fragment {
default_fields(); default_fields();
set_indicator_color(unsaved_color); set_indicator_color(unsaved_color);
}else{ }else{
get_fields(); try {
set_indicator_color(saved_color); get_fields();
set_indicator_color(saved_color);
} catch (Exception e){
AlertManager.error(e);
default_fields();
set_indicator_color(unsaved_color);
}
} }
asm.start(); asm.start();
@@ -195,11 +156,7 @@ public class PitScoutingFragment extends Fragment {
int fi = i; int fi = i;
tv.setOnClickListener(p -> { tv.setOnClickListener(p -> {
// boolean blank = !latest_values[fi].getViewValue().isNull(); update_asm();
// System.out.println(blank);
asm.update();
if(!pit_latest_values[fi].isBlank){ if(!pit_latest_values[fi].isBlank){
tv.setBackgroundColor(0xffff0000); tv.setBackgroundColor(0xffff0000);
@@ -16,7 +16,7 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.R; import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.SettingsVersionStack.latestSettings; import com.ridgebotics.ridgescout.utility.settingsManager;
import com.ridgebotics.ridgescout.databinding.FragmentScoutingBinding; import com.ridgebotics.ridgescout.databinding.FragmentScoutingBinding;
import com.ridgebotics.ridgescout.types.frcTeam; import com.ridgebotics.ridgescout.types.frcTeam;
import com.ridgebotics.ridgescout.ui.TeamSelectorFragment; import com.ridgebotics.ridgescout.ui.TeamSelectorFragment;
@@ -34,7 +34,7 @@ public class ScoutingFragment extends Fragment {
binding.buttons.setVisibility(View.VISIBLE); binding.buttons.setVisibility(View.VISIBLE);
String evcode = latestSettings.settings.get_evcode(); String evcode = settingsManager.getEVCode();
if(evcode.equals("unset")){ if(evcode.equals("unset")){
binding.noEventError.setVisibility(View.VISIBLE); binding.noEventError.setVisibility(View.VISIBLE);
@@ -40,6 +40,7 @@ public class StatusFragment extends Fragment {
} }
public static int color_found = 0x7f00ff00; public static int color_found = 0x7f00ff00;
public static int color_not_found = 0x7f7f0000; public static int color_not_found = 0x7f7f0000;
private void addTableText(TableRow tr, String textStr){ private void addTableText(TableRow tr, String textStr){
TextView text = new TextView(getContext()); TextView text = new TextView(getContext());
text.setTextSize(18); text.setTextSize(18);
@@ -1,9 +1,22 @@
package com.ridgebotics.ridgescout.ui.settings; package com.ridgebotics.ridgescout.ui.settings;
import static android.text.InputType.TYPE_CLASS_NUMBER;
import static com.ridgebotics.ridgescout.utility.settingsManager.AllyPosKey;
import static com.ridgebotics.ridgescout.utility.settingsManager.FTPEnabled;
import static com.ridgebotics.ridgescout.utility.settingsManager.FTPServer;
import static com.ridgebotics.ridgescout.utility.settingsManager.SelEVCodeKey;
import static com.ridgebotics.ridgescout.utility.settingsManager.TeamNumKey;
import static com.ridgebotics.ridgescout.utility.settingsManager.UnameKey;
import static com.ridgebotics.ridgescout.utility.settingsManager.WifiModeKey;
import static com.ridgebotics.ridgescout.utility.settingsManager.defaults;
import static com.ridgebotics.ridgescout.utility.settingsManager.getEditor;
import static com.ridgebotics.ridgescout.utility.settingsManager.prefs;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.view.Gravity;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@@ -13,22 +26,31 @@ import android.widget.CheckBox;
import android.widget.CompoundButton; import android.widget.CompoundButton;
import android.widget.EditText; import android.widget.EditText;
import android.widget.Spinner; import android.widget.Spinner;
import android.widget.TableRow;
import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import com.google.android.material.divider.MaterialDivider;
import com.ridgebotics.ridgescout.databinding.FragmentSettingsBinding; import com.ridgebotics.ridgescout.databinding.FragmentSettingsBinding;
import com.ridgebotics.ridgescout.types.data.intType;
import com.ridgebotics.ridgescout.utility.fileEditor; import com.ridgebotics.ridgescout.utility.fileEditor;
import com.ridgebotics.ridgescout.SettingsVersionStack.latestSettings; import com.ridgebotics.ridgescout.utility.settingsManager;
import com.skydoves.powerspinner.IconSpinnerAdapter; import com.skydoves.powerspinner.IconSpinnerAdapter;
import com.skydoves.powerspinner.IconSpinnerItem; import com.skydoves.powerspinner.IconSpinnerItem;
import com.skydoves.powerspinner.OnSpinnerItemSelectedListener; import com.skydoves.powerspinner.OnSpinnerItemSelectedListener;
import com.skydoves.powerspinner.PowerSpinnerView; import com.skydoves.powerspinner.PowerSpinnerView;
import com.skydoves.powerspinner.SpinnerGravity;
import org.checkerframework.checker.units.qual.C;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.stream.Stream;
public class settingsFragment extends Fragment { public class settingsFragment extends Fragment {
@@ -36,9 +58,170 @@ public class settingsFragment extends Fragment {
private android.widget.ScrollView ScrollArea; private android.widget.ScrollView ScrollArea;
private android.widget.TableLayout Table; private android.widget.TableLayout Table;
private void setDropdownItems(Spinner dropdown, String[] items){ // private void setDropdownItems(Spinner dropdown, String[] items){
ArrayAdapter<String> adapter = new ArrayAdapter<>(requireActivity(), android.R.layout.simple_spinner_item, items); // ArrayAdapter<String> adapter = new ArrayAdapter<>(requireActivity(), android.R.layout.simple_spinner_item, items);
dropdown.setAdapter(adapter); // dropdown.setAdapter(adapter);
// }
private View[] concatArrays(View[] a, View[] b){
return Stream.of(a, b).flatMap(Stream::of).toArray(View[]::new);
}
private View[] addViews(View[] a){
for(int i = 0; i < a.length; i++){
binding.SettingsTable.addView(a[i]);
}
return a;
}
private int safeToInt(String num){
if(num.isEmpty())
return 0;
try {
return Integer.parseInt(num);
}catch (NumberFormatException e){
return 0;
}
}
private View[] createHeading(String name){
TextView tv = new TextView(getContext());
tv.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
TableRow.LayoutParams params = new TableRow.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
params.topMargin = 100;
tv.setLayoutParams(params);
tv.setTextSize(20);
tv.setText(name);
View divider = new MaterialDivider(getContext());
return new View[]{tv, divider};
}
private View[] addStringEdit(String name, String key){
View[] heading = createHeading(name);
EditText et = new EditText(getContext());
et.setText(prefs.getString(key, (String) defaults.get(key)));
et.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
getEditor().putString(key, s.toString()).apply();
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
public void onTextChanged(CharSequence s, int start, int before, int count) {}
});
return concatArrays(heading, new View[]{et});
}
private View[] addNumberEdit(String name, String key){
View[] heading = createHeading(name);
EditText et = new EditText(getContext());
et.setText(String.valueOf(prefs.getInt(key, (Integer) defaults.get(key))));
et.setInputType(TYPE_CLASS_NUMBER);
et.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
getEditor().putInt(key, safeToInt(s.toString())).apply();
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
public void onTextChanged(CharSequence s, int start, int before, int count) {}
});
return concatArrays(heading, new View[]{et});
}
private PowerSpinnerView addDropdownEdit(String name, String[] options, String key){
PowerSpinnerView dropdown = new PowerSpinnerView(getContext());
List<IconSpinnerItem> iconSpinnerItems = new ArrayList<>();
for(int i = 0; i < options.length; i++){
iconSpinnerItems.add(new IconSpinnerItem(options[i]));
}
IconSpinnerAdapter iconSpinnerAdapter = new IconSpinnerAdapter(dropdown);
dropdown.setGravity(Gravity.CENTER);
dropdown.setSpinnerAdapter(iconSpinnerAdapter);
dropdown.setItems(iconSpinnerItems);
dropdown.setHint("Unselected");
dropdown.setPadding(10,20,10,20);
dropdown.setBackgroundColor(0xf0000000);
dropdown.setTextColor(0xff00ff00);
dropdown.setTextSize(14.5f);
dropdown.setArrowGravity(SpinnerGravity.END);
dropdown.setArrowPadding(8);
dropdown.setSpinnerPopupElevation(14);
return dropdown;
}
private View[] addDropdownByString(String name, String[] options, String key){
View[] heading = createHeading(name);
PowerSpinnerView dropdown = addDropdownEdit(name, options, key);
int index = Arrays.asList(options).indexOf(prefs.getString(key, (String) defaults.get(key)));
System.out.println(index);
if(options.length != 0 && index != -1){
dropdown.selectItemByIndex(index);
}
dropdown.setOnSpinnerItemSelectedListener(
(OnSpinnerItemSelectedListener<IconSpinnerItem>)
(oldIndex, oldItem, newIndex, newItem) -> getEditor().putString(key, newItem.getText().toString()).apply()
);
return concatArrays(heading, new View[]{dropdown});
}
private View[] addDropdownByIndex(String name, String[] options, String key){
View[] heading = createHeading(name);
PowerSpinnerView dropdown = addDropdownEdit(name, options, key);
int index = prefs.getInt(key, (Integer) defaults.get(key));
if(dropdown.length() != 0 && index != -1){
dropdown.selectItemByIndex(index);
}
dropdown.setOnSpinnerItemSelectedListener(
(OnSpinnerItemSelectedListener<IconSpinnerItem>)
(oldIndex, oldItem, newIndex, newItem) -> getEditor().putInt(key, newIndex).apply()
);
return concatArrays(heading, new View[]{dropdown});
}
private View[] addCheckbox(String name, String key, View[] dependency){
CheckBox cb = new CheckBox(getContext());
cb.setText(name);
cb.setTextSize(22);
boolean checked = prefs.getBoolean(key, (Boolean) defaults.get(key));
cb.setChecked(checked);
if(dependency != null && !checked){
for(int i = 0; i < dependency.length; i++){
dependency[i].setVisibility(View.GONE);
}
}
cb.setOnCheckedChangeListener((buttonView, isChecked) -> {
getEditor().putBoolean(key, isChecked).apply();
if(dependency != null){
for(int i = 0; i < dependency.length; i++){
dependency[i].setVisibility(isChecked ? View.VISIBLE : View.GONE);
System.out.println(dependency[i]);
}
}
});
return new View[]{new MaterialDivider(getContext()), cb};
} }
public View onCreateView(@NonNull LayoutInflater inflater, public View onCreateView(@NonNull LayoutInflater inflater,
@@ -47,172 +230,19 @@ public class settingsFragment extends Fragment {
binding = FragmentSettingsBinding.inflate(inflater, container, false); binding = FragmentSettingsBinding.inflate(inflater, container, false);
View root = binding.getRoot(); 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", String[] alliance_pos_list = new String[]{"red-1", "red-2", "red-3",
"blue-1", "blue-2", "blue-3"}; "blue-1", "blue-2", "blue-3"};
for(int i = 0; i < alliance_pos_list.length; i++){ addViews(addStringEdit("Username", UnameKey));
if(alliance_pos_list[i].equals(target_alliance_pos)){ addViews(addDropdownByString("Event Code", fileEditor.getEventList().toArray(new String[0]), SelEVCodeKey));
alliance_pos_target_index = i; addViews(addDropdownByString("Alliance Position", alliance_pos_list, AllyPosKey));
} addViews(addNumberEdit("Team Number", TeamNumKey));
alliance_pos_iconSpinnerItems.add(new IconSpinnerItem(alliance_pos_list[i]));
}
IconSpinnerAdapter alliance_pos_iconSpinnerAdapter = new IconSpinnerAdapter(alliance_pos_spinnerView); View[] FTPDependency = addStringEdit("FTP Server", FTPServer);
alliance_pos_spinnerView.setSpinnerAdapter(alliance_pos_iconSpinnerAdapter); View[] WifiDependency = addCheckbox("FTP Enabled", FTPEnabled, FTPDependency);
alliance_pos_spinnerView.setItems(alliance_pos_iconSpinnerItems); addViews(addCheckbox("Wifi Mode", WifiModeKey, concatArrays(FTPDependency, WifiDependency)));
alliance_pos_spinnerView.setLifecycleOwner(this); addViews(WifiDependency);
addViews(FTPDependency);
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; return root;
} }
@@ -0,0 +1,163 @@
package com.ridgebotics.ridgescout.ui.transfer;
//import static com.ridgebotics.ridgescout.utility.DataManager.evcode;
import static com.ridgebotics.ridgescout.utility.fileEditor.baseDir;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.settingsManager;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPCmd;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
public class FTPSync extends Thread {
public static final String remoteBasePath = "/RidgeScout/";
public static final long timeTolerance = 60000; // One min
public static long lastSyncTime = 0;
public interface onResult {
void onResult(boolean error, int upCount, int downCount);
}
public onResult onResult;
public static void sync(onResult onResult){
// DataManager.reload_event();
FTPSync ftpSync = new FTPSync();
ftpSync.onResult = onResult;
ftpSync.start();
lastSyncTime = new Date().getTime();
}
FTPClient ftpClient;
// private class FileDate {
// public String filename;
// public Calendar lastModified;
// }
private int upCount = 0;
private int downCount = 0;
private void downloadFile(FTPFile remoteFile, File localFile) throws IOException {
try (FileOutputStream fos = new FileOutputStream(localFile)) {
ftpClient.retrieveFile(remoteBasePath + remoteFile.getName(), fos);
}
Date d = getUtcTimestamp(remoteFile);
System.out.println(d);
setLocalFileTimestamp(localFile, d);
}
private void uploadFile(File localFile) throws IOException {
try (FileInputStream fis = new FileInputStream(localFile)) {
ftpClient.storeFile(remoteBasePath + localFile.getName(), fis);
}
setLocalFileTimestamp(localFile, new Date());
}
private FTPFile findRemoteFile(FTPFile[] remoteFiles, String fileName) {
for (FTPFile file : remoteFiles) {
if (file.getName().equals(fileName)) {
return file;
}
}
return null;
}
private Date getUtcTimestamp(FTPFile file) {
return file.getTimestamp().getTime();
}
private Date getLocalFileUtcTimestamp(File file) {
return new Date(file.lastModified());
}
private void setLocalFileTimestamp(File file, Date date) {
file.setLastModified(date.getTime());
}
// private long longAbs(long n){
// return n>0 ? n : -n;
// }
private boolean toleranceCompareAfter(Date d1, Date d2){
long diff = d1.getTime() - d2.getTime();
System.out.println(diff);
return diff > timeTolerance;
}
public void run() {
try {
// localTimeZone = TimeZone.getDefault();
ftpClient = new FTPClient();
InetAddress address = InetAddress.getByName(settingsManager.getFTPServer());
ftpClient.connect(address);
ftpClient.login("anonymous", null);
ftpClient.enterLocalPassiveMode();
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
File localDir = new File(baseDir);
File[] localFiles = localDir.listFiles();
FTPFile[] remoteFiles = ftpClient.listFiles(remoteBasePath);
if (localFiles != null) {
for (File localFile : localFiles) {
if (localFile.isFile()) {
FTPFile remoteFile = findRemoteFile(remoteFiles, localFile.getName());
//
// Date t1 = getLocalFileUtcTimestamp(localFile);
// Date t2 = getUtcTimestamp(remoteFile);
////
// System.out.println("- " + t1.getTime() + (t1.after(t2) ? ">" : "<") + t2.getTime());
if (remoteFile == null || toleranceCompareAfter(getLocalFileUtcTimestamp(localFile), (getUtcTimestamp(remoteFile)))) {
uploadFile(localFile);
System.out.println("Uploaded " + localFile.getName());
upCount++;
}else{
System.out.println("Did not upload " + localFile.getName());
}
}
}
}
for (FTPFile remoteFile : remoteFiles) {
if (!remoteFile.isDirectory()) {
File localFile = new File(baseDir, remoteFile.getName());
// Date t1 = getLocalFileUtcTimestamp(localFile);
// Date t2 = getUtcTimestamp(remoteFile);
////
// System.out.println("- " + t1 + (t1.after(t2) ? ">" : "<") + t2);
if (!localFile.exists() || toleranceCompareAfter(getUtcTimestamp(remoteFile), (getLocalFileUtcTimestamp(localFile)))) {
downloadFile(remoteFile, localFile);
System.out.println("Downloaded " + localFile.getName());
downCount++;
}else{
System.out.println("Did not download " + remoteFile.getName());
}
}
}
} catch (Exception e) {
AlertManager.error(e);
onResult.onResult(true, upCount, downCount);
} finally {
onResult.onResult(false, upCount, downCount);
}
}
}
@@ -14,11 +14,14 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.R; import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.SettingsVersionStack.latestSettings; import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.settingsManager;
import com.ridgebotics.ridgescout.databinding.FragmentTransferBinding; import com.ridgebotics.ridgescout.databinding.FragmentTransferBinding;
import com.ridgebotics.ridgescout.ui.transfer.bluetooth.BluetoothSenderFragment; import com.ridgebotics.ridgescout.ui.transfer.bluetooth.BluetoothSenderFragment;
import com.ridgebotics.ridgescout.ui.transfer.codes.CodeGeneratorView; import com.ridgebotics.ridgescout.ui.transfer.codes.CodeGeneratorView;
import java.util.Date;
public class TransferFragment extends Fragment { public class TransferFragment extends Fragment {
private FragmentTransferBinding binding; private FragmentTransferBinding binding;
@@ -45,7 +48,7 @@ public class TransferFragment extends Fragment {
binding = FragmentTransferBinding.inflate(inflater, container, false); binding = FragmentTransferBinding.inflate(inflater, container, false);
evcode = latestSettings.settings.get_evcode(); evcode = settingsManager.getEVCode();
binding.downloadButton.setOnClickListener(v -> { binding.downloadButton.setOnClickListener(v -> {
start_download(); start_download();
@@ -66,6 +69,26 @@ public class TransferFragment extends Fragment {
alert.create().show(); alert.create().show();
}); });
if(!settingsManager.getWifiMode()) {
binding.TBAButton.setEnabled(false);
binding.SyncButton.setEnabled(false);
}
if(!settingsManager.getFTPEnabled() ||
new Date().getTime()-FTPSync.lastSyncTime < FTPSync.timeTolerance) {
binding.SyncButton.setEnabled(false);
}
binding.SyncButton.setOnClickListener(v -> {
binding.SyncButton.setEnabled(false);
FTPSync.sync((error, upcount, downcount) -> getActivity().runOnUiThread(() -> {
// binding.SyncButton.setEnabled(true);
AlertManager.toast((!error ? "Synced! " : "Error Syncing. ") + upcount + " Up " + downcount + " Down");
}));
});
if(evcode.equals("unset")){ if(evcode.equals("unset")){
binding.noEventError.setVisibility(View.VISIBLE); binding.noEventError.setVisibility(View.VISIBLE);
binding.uploadButton.setEnabled(false); binding.uploadButton.setEnabled(false);
@@ -106,9 +129,6 @@ public class TransferFragment extends Fragment {
builder.show(); builder.show();
}); });
if(!latestSettings.settings.get_wifi_mode())
binding.TBAButton.setEnabled(false);
return binding.getRoot(); return binding.getRoot();
} }
@@ -74,9 +74,9 @@ public class BluetoothReceiver {
permissionsNeeded.add(Manifest.permission.BLUETOOTH_ADMIN); permissionsNeeded.add(Manifest.permission.BLUETOOTH_ADMIN);
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { // if (ContextCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
permissionsNeeded.add(Manifest.permission.ACCESS_FINE_LOCATION); // permissionsNeeded.add(Manifest.permission.ACCESS_FINE_LOCATION);
} // }
} }
} }
@@ -94,11 +94,11 @@ public class BluetoothReceiver {
boolean hasBasicPermissions = ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH) == PackageManager.PERMISSION_GRANTED && boolean hasBasicPermissions = ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH) == PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_ADMIN) == PackageManager.PERMISSION_GRANTED; ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_ADMIN) == PackageManager.PERMISSION_GRANTED;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return hasBasicPermissions && ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED; // return hasBasicPermissions && ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
} else { // } else {
return hasBasicPermissions; return hasBasicPermissions;
} // }
} }
} }
@@ -70,7 +70,7 @@ public class BluetoothSender {
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { if (ContextCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
permissionsNeeded.add(Manifest.permission.ACCESS_FINE_LOCATION); // permissionsNeeded.add(Manifest.permission.ACCESS_FINE_LOCATION);
} }
} }
} }
@@ -89,11 +89,11 @@ public class BluetoothSender {
boolean hasBasicPermissions = ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH) == PackageManager.PERMISSION_GRANTED && boolean hasBasicPermissions = ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH) == PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_ADMIN) == PackageManager.PERMISSION_GRANTED; ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_ADMIN) == PackageManager.PERMISSION_GRANTED;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return hasBasicPermissions && ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED; // return hasBasicPermissions && ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
} else { // } else {
return hasBasicPermissions; return hasBasicPermissions;
} // }
} }
} }
@@ -2,7 +2,9 @@ package com.ridgebotics.ridgescout.ui.transfer.codes;
import static androidx.core.math.MathUtils.clamp; import static androidx.core.math.MathUtils.clamp;
import android.Manifest;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.pm.PackageManager;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.media.Image; import android.media.Image;
import android.os.Bundle; import android.os.Bundle;
@@ -25,6 +27,7 @@ import androidx.camera.core.ImageProxy;
import androidx.camera.core.Preview; import androidx.camera.core.Preview;
import androidx.camera.lifecycle.ProcessCameraProvider; import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.constraintlayout.widget.ConstraintLayout; import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleOwner;
@@ -150,6 +153,9 @@ public class CodeScannerView extends Fragment {
this.lifecycle = getViewLifecycleOwner(); this.lifecycle = getViewLifecycleOwner();
if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.CAMERA}, 1);
}
uiHandler = new Handler(); uiHandler = new Handler();
@@ -2,6 +2,7 @@ package com.ridgebotics.ridgescout.utility;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
public class BuiltByteParser { public class BuiltByteParser {
public static final Integer boolType = 0; public static final Integer boolType = 0;
@@ -121,6 +122,7 @@ public class BuiltByteParser {
intArrayObject ia = new intArrayObject(); intArrayObject ia = new intArrayObject();
ia.arr = intArr; ia.arr = intArr;
System.out.println(Arrays.toString(intArr));
objects.add(ia); objects.add(ia);
break; break;
case 4: case 4:
@@ -1,6 +1,5 @@
package com.ridgebotics.ridgescout.utility; package com.ridgebotics.ridgescout.utility;
import com.ridgebotics.ridgescout.SettingsVersionStack.latestSettings;
import com.ridgebotics.ridgescout.scoutingData.fields; import com.ridgebotics.ridgescout.scoutingData.fields;
import com.ridgebotics.ridgescout.scoutingData.transfer.transferType; import com.ridgebotics.ridgescout.scoutingData.transfer.transferType;
import com.ridgebotics.ridgescout.types.frcEvent; import com.ridgebotics.ridgescout.types.frcEvent;
@@ -15,7 +14,7 @@ public class DataManager {
} }
public static String getevcode() { public static String getevcode() {
return latestSettings.settings.get_evcode(); return settingsManager.getEVCode();
} }
public static inputType[][] match_values; public static inputType[][] match_values;
@@ -5,8 +5,6 @@ import android.content.Context;
import com.ridgebotics.ridgescout.types.frcEvent; import com.ridgebotics.ridgescout.types.frcEvent;
import com.ridgebotics.ridgescout.types.frcTeam; import com.ridgebotics.ridgescout.types.frcTeam;
import com.ridgebotics.ridgescout.SettingsVersionStack.latestSettings;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
@@ -17,19 +15,25 @@ import java.io.IOException;
import java.nio.BufferOverflowException; import java.nio.BufferOverflowException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.TimeZone;
import java.util.zip.DataFormatException; import java.util.zip.DataFormatException;
import java.util.zip.Deflater; import java.util.zip.Deflater;
import java.util.zip.Inflater; import java.util.zip.Inflater;
public final class fileEditor { public final class fileEditor {
private final static String baseDir = "/data/data/com.ridgebotics.ridgescout/"; public final static String baseDir = "/data/data/com.ridgebotics.ridgescout/";
public static final byte internalDataVersion = 0x01; public static final byte internalDataVersion = 0x01;
public static final int maxCompressedBlockSize = 4096; public static final int maxCompressedBlockSize = 4096;
// private TimeZone localTimeZone = TimeZone.getDefault();
@@ -45,7 +49,6 @@ public final class fileEditor {
} }
public static char byteToChar(int num){ public static char byteToChar(int num){
return new String(toBytes(num, 1), StandardCharsets.ISO_8859_1).charAt(0); return new String(toBytes(num, 1), StandardCharsets.ISO_8859_1).charAt(0);
} }
@@ -191,7 +194,22 @@ public final class fileEditor {
// public static Calendar getLastModified(String filepath){
// File f = new File(baseDir + filepath);
// if(f.exists()){
// Calendar calendar = Calendar.getInstance();
// calendar.setTimeInMillis(f.lastModified());
// return calendar;
// }
// return null;
// }
//
// public static void setLastModified(String filepath, Calendar calendar){
// File f = new File(baseDir + filepath);
// if(f.exists()){
// f.setLastModified(calendar.getTimeInMillis());
// }
// }
@@ -200,6 +218,10 @@ public final class fileEditor {
FileOutputStream output = new FileOutputStream(baseDir + filepath); FileOutputStream output = new FileOutputStream(baseDir + filepath);
output.write(data); output.write(data);
output.close(); output.close();
// Date d = new Date();
new File(baseDir + filepath).setLastModified(new Date().getTime());
return true; return true;
} }
catch (IOException e) { catch (IOException e) {
@@ -268,8 +290,8 @@ public final class fileEditor {
public static boolean setEvent(frcEvent event){ public static boolean setEvent(frcEvent event){
final String filename = (event.eventCode + ".eventdata"); final String filename = (event.eventCode + ".eventdata");
if(latestSettings.settings.get_evcode().equals("unset")){ if(settingsManager.getEVCode().equals("unset")){
latestSettings.settings.set_evcode(event.eventCode); settingsManager.setEVCode(event.eventCode);
} }
return writeFile(filename, event.encode()); return writeFile(filename, event.encode());
@@ -0,0 +1,89 @@
package com.ridgebotics.ridgescout.utility.ollama;
import androidx.annotation.NonNull;
import com.ridgebotics.ridgescout.utility.AlertManager;
//import com.ridgebotics.ridgescout.utility.ollama.types.Messages;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okhttp3.OkHttpClient;
public class OllamaClient {
private static final String MODEL_KEY = "llama3";
private static final String OLLAMA_URL_KEY = "http://199.204.135.71:11434";
private static final String GENERATE_PATH = "/api/generate";
public interface ollamaListener {
void onResponse(String response);
void onComplete();
}
private static String promptToJson(String prompt){
try {
JSONObject jobj = new JSONObject();
jobj.put("model", "llama3");
jobj.put("prompt", prompt);
return jobj.toString();
} catch (JSONException e){
AlertManager.error(e);
}
return "{}";
}
public static void run(String prompt, ollamaListener listener){
// ChatRequest chatRequest = new ChatRequest(MODEL_KEY, llamaMessages.messages,true);
RequestBody body = RequestBody.create(promptToJson(prompt), MediaType.parse("application/json"));
Request request = new Request.Builder()
.url(OLLAMA_URL_KEY+GENERATE_PATH)
.post(body)
.build();
new OkHttpClient().newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
AlertManager.error(e);
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
responseStreamer(response, listener);
}
});
}
private static void responseStreamer(Response r, ollamaListener listener){
String line;
ResponseBody body = r.body();
if(body == null)
return;
try {
while ((line = body.source().readUtf8Line()) != null) {
// System.out.println(line);
JSONObject json = new JSONObject(line);
listener.onResponse(json.getString("response"));
if(json.getBoolean("done"))
listener.onComplete();
}
} catch (IOException | NumberFormatException | IllegalStateException | JSONException e) {
// addMessage(chatResponse,fullResponse.toString(),stopButton,sendButton);
AlertManager.error(e);
}
}
}
@@ -0,0 +1,152 @@
package com.ridgebotics.ridgescout.utility.ollama;
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 com.ridgebotics.ridgescout.scoutingData.ScoutingDataWriter;
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.frcMatch;
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.utility.DataManager;
import com.ridgebotics.ridgescout.utility.fileEditor;
public class PromptCreator {
private static String fieldSummary(inputType field){
String summary = field.name + ": ";
switch (field.getInputType()){
case DROPDOWN:
summary += "The index of a dropdown with the possible options: [" +String.join(", ", ((dropdownType) field).text_options) + "]";
break;
case SLIDER:
sliderType slider = (sliderType) field;
summary += "A slider with the range ["+slider.min+","+slider.max+"]";
break;
case TALLY:
summary += "A tally counter";
break;
case NOTES_INPUT:
summary += "Raw text input";
break;
}
return summary;
}
public static String genMatchPrompt(int matchIndex){
String prompt = "Below is a list of data collected from an FRC match. Generate a qualitative and concise summary of the teams listed, using both numerical and textual data collected in the summary. Additionally, rank the teams in order of their performance.\n\n";
frcMatch curmatch = event.matches.get(matchIndex);
prompt += "## Pit scouting\n"; prompt += "This is a list of the different fields that are present in the pit scouting data:\n";
for(int i = 0; i < pit_latest_values.length; i++){
prompt += (i+1) + ") " + fieldSummary(pit_latest_values[i]) + "\n";
}
prompt += ("\nData:\n");
for(int a = 0; a < 6; a++){
int teamNum = 0;
if(a < 3)
teamNum = curmatch.redAlliance[a];
else
teamNum = curmatch.blueAlliance[a-3];
prompt += "\nTeam " + teamNum + " pit scout data:\n";
String filename = evcode+"-"+teamNum+".pitscoutdata";
if (!fileEditor.fileExist(filename)) continue;
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++) {
boolean isNull = true;
switch (types[i].getValueType()){
case NUM:
isNull = intType.isNull((int) types[i].get());
break;
case STRING:
isNull = stringType.isNull((String) types[i].get());
break;
}
if(isNull){
prompt += match_latest_values[i].name + ": null\n";
}else{
prompt += match_latest_values[i].name + ": " + types[i].get() + "\n";
}
}
}
prompt += "\n## Match scouting\n";
prompt += "This is a list of the different fields that are present in the match scouting data:\n";
for(int i = 0; i < match_latest_values.length; i++){
prompt += (i+1) + ") " + fieldSummary(match_latest_values[i]) + "\n";
}
prompt += ("\nData:\n");
for(int a = 0; a < 6; a++){
int teamNum = 0;
if(a < 3)
teamNum = curmatch.redAlliance[a];
else
teamNum = curmatch.blueAlliance[a-3];
prompt += "\nTeam " + teamNum + " Match scout data for match " + curmatch.matchIndex +":\n";
frcMatch[] matchNums = event.getTeamMatches(teamNum);
for(int b = 0; b < matchNums.length; b++) {
frcMatch match = matchNums[b];
String alliance = "";
int alliancePos = 0;
for(int c = 0; c < 6; c++) {
if(c<3){
if(match.redAlliance[c] != teamNum) continue;
alliance = "red";
alliancePos = c+1;
break;
}else{
if(match.blueAlliance[c-3] != teamNum) continue;
alliance = "blue";
alliancePos = c-2;
break;
}
}
String filename = evcode + "-" + match.matchIndex + "-" + alliance + "-" + alliancePos + "-" + teamNum + ".matchscoutdata";
if (!fileEditor.fileExist(filename)) continue;
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++) {
boolean isNull = true;
switch (types[i].getValueType()){
case NUM:
isNull = intType.isNull((int) types[i].get());
break;
case STRING:
isNull = stringType.isNull((String) types[i].get());
break;
}
if(isNull){
prompt += match_latest_values[i].name + ": null\n";
}else{
prompt += match_latest_values[i].name + ": " + types[i].get() + "\n";
}
}
prompt += "\n";
}
}
return prompt;
}
}
@@ -0,0 +1,99 @@
package com.ridgebotics.ridgescout.utility;
import android.content.SharedPreferences;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class settingsManager {
public static SharedPreferences prefs;
public static SharedPreferences.Editor editor;
public static final String UnameKey = "username";
public static final String SelEVCodeKey = "selected_event_code";
public static final String WifiModeKey = "wifi_mode";
public static final String TeamNumKey = "team_num";
public static final String MatchNumKey = "match_num";
public static final String AllyPosKey = "alliance_pos";
public static final String DataModeKey = "data_view_mode";
public static final String BtUUIDKey = "bt_uuid";
public static final String FTPEnabled = "ftp_enabled";
public static final String FTPServer = "ftp_server";
public static Map defaults = getDefaults();
private static Map getDefaults(){
Map<String, Object> hm = new HashMap<>();
hm.put(UnameKey, "Username");
hm.put(SelEVCodeKey, "unset");
hm.put(WifiModeKey, false);
hm.put(TeamNumKey, 4388);
hm.put(MatchNumKey, 0);
hm.put(AllyPosKey, "red-1");
hm.put(DataModeKey, 0);
hm.put(BtUUIDKey, UUID.randomUUID().toString());
hm.put(FTPEnabled, false);
hm.put(FTPServer, "0.0.0.0");
return hm;
}
public static SharedPreferences.Editor getEditor(){
if(editor == null) editor = prefs.edit();
return editor;
}
public static void resetSettings(){
getEditor() .putString(UnameKey, (String) defaults.get( UnameKey )).apply();
getEditor() .putString(SelEVCodeKey,(String) defaults.get( SelEVCodeKey)).apply();
getEditor().putBoolean(WifiModeKey, (boolean) defaults.get( WifiModeKey )).apply();
getEditor() .putInt(TeamNumKey, (int) defaults.get( TeamNumKey )).apply();
getEditor() .putInt(MatchNumKey, (int) defaults.get( MatchNumKey )).apply();
getEditor() .putString(AllyPosKey, (String) defaults.get( AllyPosKey )).apply();
getEditor() .putInt(DataModeKey, (int) defaults.get( DataModeKey )).apply();
getEditor() .putString(BtUUIDKey, (String) defaults.get( BtUUIDKey )).apply();
getEditor().putBoolean(FTPEnabled, (boolean) defaults.get(FTPEnabled )).apply();
getEditor() .putString(FTPServer, (String) defaults.get( BtUUIDKey )).apply();
}
// IDK why I decided to format these functions like this. It looks cool though.
public static String getUsername(){return prefs.getString( UnameKey, (String) defaults.get(UnameKey));}
public static void setUsername(String str){ getEditor().putString( UnameKey,str).apply();}
public static String getEVCode(){return prefs.getString( SelEVCodeKey, (String) defaults.get(SelEVCodeKey));}
public static void setEVCode(String str){ getEditor().putString( SelEVCodeKey,str).apply();}
public static boolean getWifiMode(){return prefs.getBoolean( WifiModeKey, (boolean) defaults.get(WifiModeKey));}
public static void setWifiMode(boolean bool){getEditor().putBoolean( WifiModeKey,bool).apply();}
public static int getTeamNum(){return prefs.getInt( TeamNumKey, (int) defaults.get(TeamNumKey));}
public static void setTeamNum(int num){ getEditor().putInt( TeamNumKey,num).apply();}
public static int getMatchNum(){return prefs.getInt( MatchNumKey, (int) defaults.get(MatchNumKey));}
public static void setMatchNum(int num){ getEditor().putInt( MatchNumKey,num).apply();}
public static String getAllyPos(){return prefs.getString( AllyPosKey, (String) defaults.get(AllyPosKey));}
public static void setAllyPos(String str){ getEditor().putString( AllyPosKey,str).apply();}
public static int getDataMode(){return prefs.getInt( DataModeKey, (int) defaults.get(DataModeKey));}
public static void setDataMode(int num){ getEditor().putInt( DataModeKey,num).apply();}
public static String getBtUUID(){return prefs.getString( BtUUIDKey, (String) defaults.get(BtUUIDKey));}
public static void setBtUUID(String str){ getEditor().putString( BtUUIDKey,str).apply();}
public static boolean getFTPEnabled(){return prefs.getBoolean( FTPEnabled, (boolean) defaults.get(FTPEnabled));}
public static void setFTPEnabled(boolean bool){getEditor().putBoolean( FTPEnabled,bool).apply();}
public static String getFTPServer(){return prefs.getString( FTPServer, (String) defaults.get(FTPServer));}
public static void setFTPServer(String str){ getEditor().putString( FTPServer,str).apply();}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

@@ -1,33 +1,59 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<TableLayout
<TableLayout
android:id="@+id/teamMatchesTable"
android:layout_width="409dp"
android:layout_height="729dp"
android:layout_marginStart="1dp"
android:layout_marginEnd="1dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView4">
<TableRow
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent">
<TextView
android:id="@+id/team_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="1234"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/AyEyeButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="AyEye" />
<TableLayout
android:id="@+id/teamMatchesTable"
android:layout_width="409dp"
android:layout_height="wrap_content"
android:layout_marginStart="1dp"
android:layout_marginEnd="1dp"
android:stretchColumns="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/team_number">
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent" />
</TableLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="@+id/team_number">
<EditText
android:id="@+id/AyEyeBox"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</EditText>
</ScrollView>
</TableLayout> </TableLayout>
</ScrollView>
<TextView
android:id="@+id/textView4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="1234"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<TableLayout
android:id="@+id/match_table"
android:layout_width="match_parent"
android:layout_height="match_parent">
</TableLayout>
</ScrollView>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
+4 -169
View File
@@ -7,7 +7,6 @@
tools:context=".ui.settings.settingsFragment"> tools:context=".ui.settings.settingsFragment">
<ScrollView <ScrollView
android:id="@+id/ScrollArea"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginBottom="60dp" android:layout_marginBottom="60dp"
@@ -18,177 +17,13 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
<LinearLayout <TableLayout
android:id="@+id/SettingsTable"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content">
android:orientation="vertical">
</TableLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- <CheckBox-->
<!-- android:id="@+id/practice_mode"-->
<!-- android:layout_width="412dp"-->
<!-- android:layout_height="79dp"-->
<!-- android:layout_marginTop="20dp"-->
<!-- android:text="Practice Mode"-->
<!-- android:textSize="24sp"-->
<!-- app:layout_constraintEnd_toEndOf="parent"-->
<!-- app:layout_constraintHorizontal_bias="0.0"-->
<!-- app:layout_constraintStart_toStartOf="parent"-->
<!-- app:layout_constraintTop_toBottomOf="@+id/eventDropdown" />-->
<TextView
android:id="@+id/textView2"
android:layout_width="66dp"
android:layout_height="24dp"
android:layout_marginTop="16dp"
android:text="Name"
android:textAlignment="center"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/username"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="text"
android:text="Username"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView2" />
<TextView
android:id="@+id/textView1"
android:layout_width="107dp"
android:layout_height="24dp"
android:layout_marginStart="152dp"
android:layout_marginTop="20dp"
android:layout_marginEnd="152dp"
android:text="Event Code"
android:textAlignment="center"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/username" />
<com.skydoves.powerspinner.PowerSpinnerView
android:id="@+id/eventDropdown"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/black_2"
android:gravity="center"
android:hint="No events selected"
android:padding="10dp"
android:textColor="@color/main_500"
android:textColorHint="@color/teal_700"
android:textSize="14.5sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView1"
app:spinner_arrow_gravity="end"
app:spinner_arrow_padding="8dp"
app:spinner_divider_color="@color/teal_200"
app:spinner_divider_show="true"
app:spinner_divider_size="0.4dp"
app:spinner_popup_background="@color/black_2"
app:spinner_popup_elevation="14dp" />
<TextView
android:id="@+id/alliance_pos_text"
android:layout_width="107dp"
android:layout_height="24dp"
android:layout_marginStart="152dp"
android:layout_marginTop="20dp"
android:layout_marginEnd="152dp"
android:text="Alliance Position"
android:textAlignment="center"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/eventDropdown" />
<com.skydoves.powerspinner.PowerSpinnerView
android:id="@+id/alliance_pos_dropdown"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/black_2"
android:gravity="center"
android:hint="No events selected"
android:padding="10dp"
android:textColor="@color/main_500"
android:textColorHint="@color/teal_700"
android:textSize="14.5sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/alliance_pos_text"
app:spinner_arrow_gravity="end"
app:spinner_arrow_padding="8dp"
app:spinner_divider_color="@color/teal_200"
app:spinner_divider_show="true"
app:spinner_divider_size="0.4dp"
app:spinner_popup_background="@color/black_2"
app:spinner_popup_elevation="14dp" />
<TextView
android:id="@+id/team_num_settings_label"
android:layout_width="107dp"
android:layout_height="24dp"
android:layout_marginStart="152dp"
android:layout_marginTop="20dp"
android:layout_marginEnd="152dp"
android:text="Team number"
android:textAlignment="center"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/alliance_pos_dropdown" />
<EditText
android:id="@+id/team_number"
android:layout_width="193dp"
android:layout_height="65dp"
android:ems="10"
android:gravity="center"
android:inputType="number"
android:padding="10dp"
android:text="4388"
android:textColor="@color/main_500"
android:textColorHint="@color/teal_700"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/team_num_settings_label" />
<CheckBox
android:id="@+id/wifi_mode"
android:layout_width="412dp"
android:layout_height="79dp"
android:layout_marginTop="24dp"
android:text="Wifi Mode"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/team_number" />
<Button
android:id="@+id/reset_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Reset Settings"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</ScrollView> </ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
+18 -5
View File
@@ -33,7 +33,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Upload" android:text="Upload"
android:textSize="34sp" android:textSize="34sp"
app:layout_constraintBottom_toTopOf="@+id/CSVButton" app:layout_constraintBottom_toTopOf="@+id/SyncButton"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
@@ -44,21 +44,34 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Download" android:text="Download"
android:textSize="34sp" android:textSize="34sp"
app:layout_constraintBottom_toTopOf="@+id/CSVButton" app:layout_constraintBottom_toTopOf="@+id/SyncButton"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/uploadButton" /> app:layout_constraintTop_toBottomOf="@+id/uploadButton" />
<Button
android:id="@+id/SyncButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SYNC"
android:textSize="34sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button <Button
android:id="@+id/CSVButton" android:id="@+id/CSVButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="CSV" android:text="CSV"
android:textSize="34sp" android:textSize="34sp"
app:layout_constraintBottom_toBottomOf="@+id/TBAButton" app:layout_constraintBottom_toTopOf="@+id/TBAButton"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/uploadButton" /> app:layout_constraintTop_toBottomOf="@+id/SyncButton" />
<Button <Button
android:id="@+id/TBAButton" android:id="@+id/TBAButton"
@@ -69,7 +82,7 @@
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/CSVButton" /> app:layout_constraintTop_toBottomOf="@+id/SyncButton" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
@@ -26,7 +26,11 @@
app:popExitAnim="@anim/pop_exit_anim" /> app:popExitAnim="@anim/pop_exit_anim" />
<action <action
android:id="@+id/action_navigation_scouting_to_navigation_scouting_status" android:id="@+id/action_navigation_scouting_to_navigation_scouting_status"
app:destination="@id/navigation_scouting_status" /> app:destination="@id/navigation_scouting_status"
app:enterAnim="@anim/enter_anim"
app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/pop_exit_anim"/>
</fragment> </fragment>
<fragment <fragment
@@ -77,13 +81,6 @@
app:exitAnim="@anim/exit_anim" app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim" app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/pop_exit_anim" /> app:popExitAnim="@anim/pop_exit_anim" />
<action
android:id="@+id/action_navigation_data_to_navigation_data_report"
app:destination="@id/navigation_data_report"
app:enterAnim="@anim/enter_anim"
app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/pop_exit_anim" />
<action <action
android:id="@+id/action_navigation_data_to_navigation_data_fields_chooser" android:id="@+id/action_navigation_data_to_navigation_data_fields_chooser"
app:destination="@id/navigation_data_fields_chooser" app:destination="@id/navigation_data_fields_chooser"
@@ -91,13 +88,20 @@
app:exitAnim="@anim/exit_anim" app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim" app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/pop_exit_anim" /> app:popExitAnim="@anim/pop_exit_anim" />
<action
android:id="@+id/action_navigation_data_to_navigation_data_report_selector"
app:destination="@id/navigation_data_report_selector"
app:enterAnim="@anim/enter_anim"
app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/pop_exit_anim"/>
<action <action
android:id="@+id/action_navigation_data_to_navigation_data_compare" android:id="@+id/action_navigation_data_to_navigation_data_compare"
app:destination="@id/navigation_data_compare" app:destination="@id/navigation_data_compare"
app:enterAnim="@anim/enter_anim" app:enterAnim="@anim/enter_anim"
app:exitAnim="@anim/exit_anim" app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim" app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/pop_exit_anim" /> app:popExitAnim="@anim/pop_exit_anim"/>
</fragment> </fragment>
<fragment <fragment
@@ -143,7 +147,27 @@
tools:layout="@layout/fragment_data_fields"> tools:layout="@layout/fragment_data_fields">
<action <action
android:id="@+id/action_navigation_data_fields_to_navigation_data_fields_chooser" android:id="@+id/action_navigation_data_fields_to_navigation_data_fields_chooser"
app:destination="@id/navigation_data_fields_chooser" /> app:destination="@id/navigation_data_fields_chooser"
app:enterAnim="@anim/enter_anim"
app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/pop_exit_anim" />
</fragment>
<fragment
android:id="@+id/navigation_data_report_selector"
android:name="com.ridgebotics.ridgescout.ui.data.ReportSelectorFragment"
tools:layout="@layout/fragment_data_report_selector">
<action
android:id="@+id/action_navigation_data_report_selector_to_navigation_data_report"
app:destination="@id/navigation_data_report"
app:enterAnim="@anim/enter_anim"
app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/pop_exit_anim"/>
<action
android:id="@+id/action_navigation_data_report_selector_to_navigation_data"
app:destination="@id/navigation_data" />
</fragment> </fragment>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
</network-security-config>
+2
View File
@@ -11,6 +11,7 @@ lifecycleViewmodelKtx = "2.6.1"
navigationFragment = "2.6.0" navigationFragment = "2.6.0"
navigationUi = "2.6.0" navigationUi = "2.6.0"
supportAnnotations = "28.0.0" supportAnnotations = "28.0.0"
preference = "1.2.1"
[libraries] [libraries]
junit = { group = "junit", name = "junit", version.ref = "junit" } junit = { group = "junit", name = "junit", version.ref = "junit" }
@@ -24,6 +25,7 @@ lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-view
navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment", version.ref = "navigationFragment" } navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment", version.ref = "navigationFragment" }
navigation-ui = { group = "androidx.navigation", name = "navigation-ui", version.ref = "navigationUi" } navigation-ui = { group = "androidx.navigation", name = "navigation-ui", version.ref = "navigationUi" }
support-annotations = { group = "com.android.support", name = "support-annotations", version.ref = "supportAnnotations" } support-annotations = { group = "com.android.support", name = "support-annotations", version.ref = "supportAnnotations" }
preference = { group = "androidx.preference", name = "preference", version.ref = "preference" }
[plugins] [plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" } androidApplication = { id = "com.android.application", version.ref = "agp" }
+2 -2
View File
@@ -1,3 +1,3 @@
Work in progress! This is a scouting app for First Robotics Compitition matches by Ridgebotics, that includes many features.
This is a scouting app for First Robotics Compitition matches, that includes many features. Wiki: https://github.com/team4388/ScoutingApp2025/wiki

Before

Width:  |  Height:  |  Size: 373 KiB

After

Width:  |  Height:  |  Size: 373 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB