34 Commits

Author SHA1 Message Date
Michael Mikovsky 3e045bfcb7 Field selector, Field back button, TBA popup 2025-03-07 12:24:16 -07:00
Michael Mikovsky c78fa58754 Colors and adaptable icon 2025-03-07 11:17:02 -07:00
Michael Mikovsky 18748301cd Merge pull request #3 from Team4388/Fix-CSV-Export
Fix CSV Export
2025-03-06 18:48:05 +00:00
Michael Mikovsky a15deda539 Fix CSV Export 2025-03-06 11:46:12 -07:00
Michael Mikovsky 0846f5a3b3 Increment version number again 2025-03-01 00:06:53 -07:00
Michael Mikovsky 554cad2abd Fix Lint error 2 2025-03-01 00:00:44 -07:00
Michael Mikovsky 7d41a5e5a9 Fix Lint error 1 2025-02-28 23:55:02 -07:00
Michael Mikovsky 4cb20f4769 Forgot to change the build number 2025-02-28 22:55:20 -07:00
Michael Mikovsky 6b4b919e5f Change Fields 2025-02-28 22:54:02 -07:00
Michael Mikovsky 9440583f78 Merge pull request #1 from Team4388/overhaul-ui
Overhaul UI
2025-02-27 18:15:55 +00:00
Michael Mikovsky ea9ea39368 Change pit fields 2025-02-26 11:07:44 -07:00
Michael Mikovsky bf72d1cbe5 Update version number, fix FTP Sync 2025-02-25 12:12:15 -07:00
Michael Mikovsky f3ce5a6e55 Make the field image not slow the tablets 2025-02-21 11:16:47 -07:00
Michael Mikovsky 8e2c491273 Fix checkbox bug, improve data layout 2025-02-20 12:57:14 -07:00
Michael Mikovsky 38ccf17281 Add event editing 2025-02-18 15:04:49 -07:00
Michael Mikovsky 46af23909f Work on adding practice mode 2025-02-18 08:34:27 -07:00
Michael Mikovsky cf3856805b Rework some UI elements 2025-02-17 22:47:39 -07:00
Michael Mikovsky ff84760ab2 Add better dropdown 2025-02-17 15:46:00 -07:00
Michael Mikovsky 1df7928da1 New background, stuff with dropdowns 2025-02-16 23:31:27 -07:00
Michael Mikovsky f701397577 Fix crash with Fields 2025-02-14 11:44:05 -07:00
Michael Mikovsky b264a3bb73 Add checkbox 2025-02-02 11:50:24 -07:00
Michael Mikovsky 8381d21eff Add background, fix a crash 2025-01-24 14:12:17 -07:00
Michael Mikovsky 19e377fd9e Fix crash 2025-01-23 12:29:45 -07:00
Michael Mikovsky 1a4ccf4f92 Add meta files 2025-01-23 11:58:45 -07:00
Astatin3 78620d2031 Merge branch 'main' of https://github.com/Team4388/ScoutingApp2025 2024-10-15 17:48:59 -06:00
Astatin3 15be86453e Update version number 2024-10-12 20:43:37 -06:00
Astatin3 795fa4e85a KCMT Hotfix 2024-10-12 20:37:18 -06:00
Astatin3 86432693aa Make FTP Sync better 2024-10-08 15:29:08 -06:00
Astatin3 fecf7e5d2b Work on improving ftp timestamp 2024-10-07 20:16:42 -06:00
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
89 changed files with 2927 additions and 1894 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
+15 -7
View File
@@ -1,6 +1,15 @@
![Ridgescout](https://github.com/Team4388/ScoutingApp2025/blob/main/metadata/en-US/images/FeatureGraphic.png?raw=true) ![Ridgescout](https://github.com/Team4388/ScoutingApp2025/blob/main/metadata/en-US/images/featureGraphic.png?raw=true)
#### Docs are yet to be written, but here is an overview of the main features currently included in the app: [<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
alt="Get it on F-Droid"
height="80">](https://f-droid.org/packages/com.ridgebotics.ridgescout/)
**Note**: The F-Droid version of this app is not currently up to date with the GitHub release
[**Read the wiki**](https://github.com/Team4388/ScoutingApp2025/wiki)
[**Test Data**](https://github.com/Team4388/ScoutingApp2025/blob/main/2024week0-1728149849985.scoutbundle)
#### Here is an overview of the main features currently included in the app:
- This project is written for Android! No need for some kind of janky laptop charging setup. - This project is written for Android! No need for some kind of janky laptop charging setup.
- Similar to ScoutingPASS, there are many diffrent types of fields that can be used to collect data. - Similar to ScoutingPASS, 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! - The app is designed to handle updates to the fields on the fly, without loosing any data!
@@ -8,16 +17,14 @@
- Dynamic displays based off of the diffrent fields. - Dynamic displays based off of the diffrent fields.
- Data transfer including 2D codes, Bluetooth, and File Bundle. - Data transfer including 2D codes, Bluetooth, and File Bundle.
- Exporting using CSV. - Exporting using CSV.
- Deployment on F-Droid
- Data cloud sync using an FTP server
#### Things that are yet to be implemented: #### Things that are yet to be implemented:
- A page that lets users cross-compare scouting data between teams. (Compare) - 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) - A page that lets scouters more easily make reports to the drive team before a match starts (Report)
- More types of fields
- Data cloud sync using an FTP server
- Deployment on F-Droid
#### Things that may or may not be implemented: #### Things that may or may not be implemented:
- Practice mode
- Statbotics intgration - Statbotics intgration
- Scout error estimation using OPR-like calculation - Scout error estimation using OPR-like calculation
- - Would most likely require Statbotics - - Would most likely require Statbotics
@@ -25,4 +32,5 @@
### Screenshots ### Screenshots
|Match scouting interface|Field editor|Teams data viewer| |Match scouting interface|Field editor|Teams data viewer|
|-|-|-| |-|-|-|
|![Screenshot1](https://github.com/Team4388/ScoutingApp2025/blob/main/Images/Screenshot_20240925-142944_RidgeScout_1.png?raw=true)|![Screenshot2](https://github.com/Team4388/ScoutingApp2025/blob/main/Images/Screenshot_20240925-143135_RidgeScout_1.png?raw=true)|![Screenshot3](https://github.com/Team4388/ScoutingApp2025/blob/main/Images/Screenshot_20240925-143522_RidgeScout_1.png?raw=true)| |![Screenshot1](https://github.com/Team4388/ScoutingApp2025/blob/main/metadata/en-US/images/phoneScreenshots/1.png?raw=true)|![Screenshot2](https://github.com/Team4388/ScoutingApp2025/blob/main/metadata/en-US/images/phoneScreenshots/2.png?raw=true)|![Screenshot3](https://github.com/Team4388/ScoutingApp2025/blob/main/metadata/en-US/images/phoneScreenshots/3.png?raw=true)|
+9 -23
View File
@@ -1,38 +1,24 @@
### TODO: ### TODO:
##### Scouting: ##### Scouting:
- Make practice mode??
##### Data Analysis: ##### Data Analysis:
- Statbotics intigration??? - Statbotics intigration???
- Make the "Compare" menu, cross comparing team's stats.
##### Functionality: ##### Functionality:
- Add more types of data fields. - Test new FTP thing
- Test the scouting app - UUIDs instead of names for the fields
- Write docs - Fix data storage crashes
- Match selector instead of list for individual team views
### In Progress: ### In Progress:
##### Scouting: ##### Scouting:
- Make scouting UI look much better
##### Data Analysis: ##### 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.
##### Functionality: ##### Functionality:
- Make server software to allow for easy sync over wifi - FTP
- Deploy to F-Droid
### Done: ### Done:
##### Scouting: ##### Scouting:
- Add an "unselect" option to all of the scouting fields - Make practice mode
- When a field is created, make updated scouting data return null values, not the default value - Description for fields
- Fix scouting offset bug
##### Data Analysis: ##### 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: ##### Functionality:
- Improve the code scanning progress indicator. It has a rounding error, I think. - "Send Meta Files" button
- Fix navigation crashes. - Year selector
- 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.
+5 -8
View File
@@ -1,6 +1,3 @@
import com.android.build.api.dsl.AaptOptions
import com.android.build.api.dsl.AndroidResources
plugins { plugins {
alias(libs.plugins.androidApplication) alias(libs.plugins.androidApplication)
// id("com.google.gms.google-services") // id("com.google.gms.google-services")
@@ -28,8 +25,8 @@ android {
applicationId = "com.ridgebotics.ridgescout" applicationId = "com.ridgebotics.ridgescout"
minSdk = 24 minSdk = 24
targetSdk = 34 targetSdk = 34
versionCode = 4 versionCode = 9 // **IMPORTANT** Increment this before releasing on github
versionName = "0.4" versionName = "1.2"// **IMPORTANT** Change this before releasing on github (<Year num since 2024>.<Update Version>)
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }
@@ -56,11 +53,13 @@ dependencies {
implementation(libs.appcompat) implementation(libs.appcompat)
implementation(libs.material) implementation(libs.material)
implementation(libs.material3)
implementation(libs.constraintlayout) implementation(libs.constraintlayout)
implementation(libs.lifecycle.livedata.ktx) implementation(libs.lifecycle.livedata.ktx)
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)
@@ -75,18 +74,16 @@ dependencies {
implementation("com.journeyapps:zxing-android-embedded:4.3.0") implementation("com.journeyapps:zxing-android-embedded:4.3.0")
implementation("com.github.skydoves:powerspinner:1.2.7")
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("com.squareup.okhttp3:okhttp:4.9.0") 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": 4,
"versionName": "0.4",
"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
}
+1 -1
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" />
@@ -25,6 +25,7 @@ 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 {
@@ -51,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);
@@ -85,9 +89,8 @@ public class MainActivity extends AppCompatActivity {
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration); NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
NavigationUI.setupWithNavController(navView, navController); NavigationUI.setupWithNavController(navView, navController);
navView.setOnItemSelectedListener(new NavigationBarView.OnItemSelectedListener() { navView.setOnItemSelectedListener(item -> {
@Override backPressed = null;
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
clearBackStack(); clearBackStack();
navController.navigate(item.getItemId(), savedInstanceState, new NavOptions.Builder() navController.navigate(item.getItemId(), savedInstanceState, new NavOptions.Builder()
.setEnterAnim(R.anim.enter_anim) .setEnterAnim(R.anim.enter_anim)
@@ -96,7 +99,6 @@ public class MainActivity extends AppCompatActivity {
.setPopExitAnim(R.anim.pop_exit_anim).build() .setPopExitAnim(R.anim.pop_exit_anim).build()
); );
return true; return true;
}
}); });
} }
@@ -112,11 +114,9 @@ public class MainActivity extends AppCompatActivity {
public interface activityResultRelay { public interface activityResultRelay {
void onActivityResult(int requestCode, int resultCode, Intent data); void onActivityResult(int requestCode, int resultCode, Intent data);
} }
public static activityResultRelay resultRelay = null; public static activityResultRelay resultRelay = null;
public static void setResultRelay(activityResultRelay tmpresultRelay){ public static void setResultRelay(activityResultRelay tmpresultRelay){
resultRelay = tmpresultRelay; resultRelay = tmpresultRelay;
@@ -131,4 +131,25 @@ public class MainActivity extends AppCompatActivity {
} }
} }
public interface onBackPressed {
boolean onBackPressed();
}
public onBackPressed backPressed = null;
public void setOnBackPressed(onBackPressed onBackPressed){
this.backPressed = onBackPressed;
}
@Override
public void onBackPressed() {
if(backPressed != null) {
if (backPressed.onBackPressed()) {
super.onBackPressed();
}
} else {super.onBackPressed();}
}
} }
@@ -23,27 +23,64 @@ 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 fieldposType("Auto start pos", "Where does the robot start its auto?", new int[]{0,0}),
new tallyType("Auto Notes", 0),
new sliderType("Auto Performance", 5, 0, 10), new tallyType("Auto L4 Coral", "How many coral did this robot score in L4 during auto?", 0),
new textType("Auto Comments", ""), new tallyType("Auto L3 Coral", "How many coral did this robot score in L3 during auto?", 0),
new tallyType("Teleop Notes", 0), new tallyType("Auto L2 Coral", "How many coral did this robot score in L2 during auto?", 0),
new sliderType("Teleop Performance", 5, 0, 10), new tallyType("Auto L1/Trough Coral", "How many coral did this robot score in L1 during auto?", 0),
new textType("Teleop Comments", ""), new tallyType("Auto Processor Algae", "How many algae did this robot score in the Barge during auto?", 0),
new sliderType("Overall Driving Performance", 5, 0, 10), new tallyType("Auto Barge Algae", "How many algae did this robot score in the Barge during auto?", 0),
new textType("Overall Driving Comments", ""),
new sliderType("Score area (AMP <-> Speaker)", 5, 0, 10), new dropdownType("Auto Quality", "How did the robot drive during auto?", new String[]{"Smooth", "Jittery"}, 0),
new dropdownType("End Condition", new String[]{"Nothing", "Attempted Climb", "Successful Climbed", "Climbed with multiple robots", "Climbed with trap"}, 0), new textType("Auto Comments", "Anything interesting about auto", ""),
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 tallyType("Teleop L4 Coral", "How many coral did this robot score in L4 during auto?", 0),
new tallyType("Teleop L3 Coral", "How many coral did this robot score in L3 during auto?", 0),
new tallyType("Teleop L2 Coral", "How many coral did this robot score in L2 during auto?", 0),
new tallyType("Teleop L1 Coral", "How many coral did this robot score in L1 during auto?", 0),
new tallyType("Teleop Processor Algae", "How many algae did this robot score in the Barge during auto?", 0),
new tallyType("Teleop Barge Algae", "How many algae did this robot score in the Barge during auto?", 0),
new checkboxType("Upper Algae Removal", "Did the robot remove upper Algae?", 0),
new checkboxType("Lower Algae Removal", "Did the robot remove lower Algae?", 0),
new dropdownType("Teleop Quality", "How did the robot drive during Teleop?", new String[]{"Smooth", "Jittery"}, 0),
new textType("Teleop Comments", "Anything interesting about Teleop", ""),
new dropdownType("Climb State", "What was the final condition of the robot?", new String[]{"Nothing", "Continued Cycling", "Park", "Attempted Shallow", "Shallow", "Attempted Deep", "Deep"}, 0),
new dropdownType("Robot Condition", "Was anything broken?", new String[]{"Everything was working", "Something was maybe broken", "Something was broken", "Robot was disabled for part of the match", "Missing robot"}, 0),
new textType("Other Comments", "Any other comments you have", "")
} }
}; };
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 dropdownType("Drivetrain type", "What type of drivetrain does this team have?", new String[]{"Swerve Drive", "Tank Drive (Differential)", "Other, Info in comments"}, 0),
new sliderType("Test", 1, 0, 10), new dropdownType("Intake type", "What type of intake does this team have?", new String[]{"Ground only", "Player Station only", "Both", "Other, Info in comments"}, 0),
new textType("notes", ""), new dropdownType("Intake Consistency", "How consistent is the robot at intakeing?", new String[]{"Does not work", "Worked a few times during testing", "Works most of the time", "Fails sometimes", "Never fails"}, 0),
new dropdownType("Score Area", "What does this robot score?", new String[]{"Only Algae", "Mostly Algae", "Both", "Mostly Coral", "Only Coral"}, 0),
new checkboxType("L4 Scoring", "Will the robot score in Layer 4?", 0),
new checkboxType("L3 Scoring", "Will the robot score in Layer 3?", 0),
new checkboxType("L2 Scoring", "Will the robot score in Layer 3?", 0),
new checkboxType("L1/Trough Scoring", "Will the robot score in Layer 1?", 0),
new checkboxType("Processor Scoring", "Will the robot score in the Processor?", 0),
new checkboxType("Barge Scoring", "Will the robot score algae in the Barge?", 0),
new dropdownType("Scoring Consistency", "How consistent is the robot at Scoring?", new String[]{"Does not work", "Worked a few times during testing", "Works most of the time", "Fails sometimes", "Never fails"}, 0),
new textType("Auto Capability", "What autos does this team have?", ""),
new dropdownType("Auto Consistency", "How consistent is the robot at Auto?", new String[]{"Does not work", "Worked a few times during testing", "Works most of the time", "Fails sometimes", "Never fails"}, 0),
new dropdownType("Climb type", "What does the robot do to climb?", new String[]{"No Climb", "Only Shallow", "Only Deep", "Both Shallow and Deep"}, 0),
new dropdownType("Climb Consistency", "How consistent is the robot at climbing?", new String[]{"Does not work", "Worked a few times during testing", "Works most of the time", "Fails sometimes", "Never fails"}, 0),
new textType("Cool Comments", "Is there anything cool about the robot?", ""),
new textType("Comments", "Things go here", "Day 1:\n\nDay 2:\n\nDay 3:\n")
} }
}; };
@@ -1,5 +1,9 @@
package com.ridgebotics.ridgescout.types.data; package com.ridgebotics.ridgescout.types.data;
import androidx.annotation.NonNull;
import org.jetbrains.annotations.NotNull;
public abstract class dataType { public abstract class dataType {
public enum valueTypes { public enum valueTypes {
NUM, NUM,
@@ -1,5 +1,6 @@
package com.ridgebotics.ridgescout.types.input; package com.ridgebotics.ridgescout.types.input;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.graphics.Color; import android.graphics.Color;
import android.view.Gravity; import android.view.Gravity;
@@ -28,11 +29,6 @@ import com.ridgebotics.ridgescout.types.data.dataType;
import com.ridgebotics.ridgescout.types.data.intType; import com.ridgebotics.ridgescout.types.data.intType;
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 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.ArrayList;
import java.util.List; import java.util.List;
@@ -45,8 +41,8 @@ public class checkboxType extends inputType {
public Object get_fallback_value(){return 0;} public Object get_fallback_value(){return 0;}
public checkboxType(){}; public checkboxType(){};
public String get_type_name(){return "Checkbox";} public String get_type_name(){return "Checkbox";}
public checkboxType(String name, int isChecked){ public checkboxType(String name, String description, int isChecked){
super(name); super(name, description);
this.default_value = isChecked; this.default_value = isChecked;
} }
@@ -54,6 +50,7 @@ public class checkboxType extends inputType {
public byte[] encode() throws ByteBuilder.buildingException { public byte[] encode() throws ByteBuilder.buildingException {
ByteBuilder bb = new ByteBuilder(); ByteBuilder bb = new ByteBuilder();
bb.addString(name); bb.addString(name);
bb.addString(description);
bb.addInt((int)default_value); bb.addInt((int)default_value);
return bb.build(); return bb.build();
} }
@@ -62,7 +59,8 @@ public class checkboxType extends inputType {
ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse(); ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse();
name = (String) objects.get(0).get(); name = (String) objects.get(0).get();
default_value = objects.get(1).get(); description = (String) objects.get(1).get();
default_value = objects.get(2).get();
} }
// public PowerSpinnerView dropdown = null; // public PowerSpinnerView dropdown = null;
@@ -71,15 +69,11 @@ public class checkboxType extends inputType {
public View createView(Context context, Function<dataType, Integer> onUpdate){ public View createView(Context context, Function<dataType, Integer> onUpdate){
checkBox = new CheckBox(context); checkBox = new CheckBox(context);
checkBox.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Headline6);
checkBox.setText(name); checkBox.setText(name);
checkBox.setTextSize(24);
setViewValue(default_value); setViewValue(default_value);
checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { checkBox.setOnCheckedChangeListener((buttonView, isChecked) -> onUpdate.apply(getViewValue()));
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
onUpdate.apply(getViewValue());
}
});
return checkBox; return checkBox;
@@ -114,6 +108,7 @@ public class checkboxType extends inputType {
public void add_individual_view(LinearLayout parent, dataType data){ public void add_individual_view(LinearLayout parent, dataType data){
if(data.isNull()) return; if(data.isNull()) return;
CheckBox cb = new CheckBox(parent.getContext()); CheckBox cb = new CheckBox(parent.getContext());
cb.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Headline6);
cb.setText(name); cb.setText(name);
cb.setChecked((int) data.get() == 1); cb.setChecked((int) data.get() == 1);
cb.setEnabled(false); cb.setEnabled(false);
@@ -224,5 +219,9 @@ public class checkboxType extends inputType {
chart.invalidate(); chart.invalidate();
parent.addView(chart); parent.addView(chart);
} }
public String toString(dataType data){
return (int) data.get() == 1 ? "true" : "false";
}
} }
@@ -1,5 +1,8 @@
package com.ridgebotics.ridgescout.types.input; package com.ridgebotics.ridgescout.types.input;
import static com.google.android.material.internal.ContextUtils.getActivity;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.graphics.Color; import android.graphics.Color;
import android.view.Gravity; import android.view.Gravity;
@@ -13,6 +16,7 @@ import androidx.annotation.Nullable;
import com.ridgebotics.ridgescout.types.data.dataType; import com.ridgebotics.ridgescout.types.data.dataType;
import com.ridgebotics.ridgescout.types.data.intType; import com.ridgebotics.ridgescout.types.data.intType;
import com.ridgebotics.ridgescout.ui.CustomSpinnerView;
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 com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.charts.LineChart;
@@ -24,13 +28,9 @@ import com.github.mikephil.charting.data.LineDataSet;
import com.github.mikephil.charting.data.PieData; import com.github.mikephil.charting.data.PieData;
import com.github.mikephil.charting.data.PieDataSet; import com.github.mikephil.charting.data.PieDataSet;
import com.github.mikephil.charting.data.PieEntry; import com.github.mikephil.charting.data.PieEntry;
import com.skydoves.powerspinner.IconSpinnerAdapter;
import com.skydoves.powerspinner.IconSpinnerItem;
import com.skydoves.powerspinner.OnSpinnerItemSelectedListener;
import com.skydoves.powerspinner.PowerSpinnerView;
import com.skydoves.powerspinner.SpinnerGravity;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.function.Function; import java.util.function.Function;
@@ -42,8 +42,8 @@ public class dropdownType extends inputType {
public Object get_fallback_value(){return 0;} public Object get_fallback_value(){return 0;}
public dropdownType(){}; public dropdownType(){};
public String get_type_name(){return "Dropdown";} public String get_type_name(){return "Dropdown";}
public dropdownType(String name, String[] text_options, int defaultSelIndex){ public dropdownType(String name, String description, String[] text_options, int defaultSelIndex){
super(name); super(name, description);
this.text_options = text_options; this.text_options = text_options;
this.default_value = defaultSelIndex; this.default_value = defaultSelIndex;
} }
@@ -52,6 +52,7 @@ public class dropdownType extends inputType {
public byte[] encode() throws ByteBuilder.buildingException { public byte[] encode() throws ByteBuilder.buildingException {
ByteBuilder bb = new ByteBuilder(); ByteBuilder bb = new ByteBuilder();
bb.addString(name); bb.addString(name);
bb.addString(description);
bb.addInt((int)default_value); bb.addInt((int)default_value);
bb.addStringArray(text_options); bb.addStringArray(text_options);
return bb.build(); return bb.build();
@@ -61,55 +62,24 @@ public class dropdownType extends inputType {
ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse(); ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse();
name = (String) objects.get(0).get(); name = (String) objects.get(0).get();
default_value = objects.get(1).get(); description = (String) objects.get(1).get();
text_options = (String[]) objects.get(2).get(); default_value = objects.get(2).get();
text_options = (String[]) objects.get(3).get();
} }
public PowerSpinnerView dropdown = null; public CustomSpinnerView dropdown = null;
public View createView(Context context, Function<dataType, Integer> onUpdate){ public View createView(Context context, Function<dataType, Integer> onUpdate){
dropdown = new PowerSpinnerView(context); dropdown = new CustomSpinnerView(context);
List<IconSpinnerItem> iconSpinnerItems = new ArrayList<>(); ArrayList<String> iconSpinnerItems = new ArrayList<>(Arrays.asList(text_options));
for(int i = 0; i < text_options.length; i++){
iconSpinnerItems.add(new IconSpinnerItem(text_options[i]));
}
IconSpinnerAdapter iconSpinnerAdapter = new IconSpinnerAdapter(dropdown);
dropdown.setGravity(Gravity.CENTER); dropdown.setTitle(name);
dropdown.setOptions(iconSpinnerItems, (int) default_value);
dropdown.setSpinnerAdapter(iconSpinnerAdapter);
dropdown.setItems(iconSpinnerItems);
dropdown.selectItemByIndex((int) default_value);
dropdown.setPadding(10,20,10,20);
dropdown.setBackgroundColor(0xf0000000);
dropdown.setTextColor(0xff00ff00);
dropdown.setTextSize(14.5f);
dropdown.setArrowGravity(SpinnerGravity.END);
dropdown.setArrowPadding(8);
// dropdown.setSpinnerItemHeight(46);
dropdown.setSpinnerPopupElevation(14);
dropdown.setOnSpinnerItemSelectedListener(new OnSpinnerItemSelectedListener<IconSpinnerItem>() {
@Override
public void onItemSelected(int oldIndex, @Nullable IconSpinnerItem oldItem, int newIndex,
IconSpinnerItem newItem) {
onUpdate.apply(getViewValue()); onUpdate.apply(getViewValue());
}
});
// dropdown.setLifecycleOwner(context.life); dropdown.setOnClickListener((item, index) -> onUpdate.apply(getViewValue()));
// slider.addOnChangeListener(new Slider.OnChangeListener() {
// @Override
// public void onValueChange(@NonNull Slider slider, float value, boolean fromUser) {
// onUpdate.apply(getViewValue());
// }
// });
return dropdown; return dropdown;
} }
@@ -123,7 +93,7 @@ public class dropdownType extends inputType {
isBlank = false; isBlank = false;
dropdown.setVisibility(View.VISIBLE); dropdown.setVisibility(View.VISIBLE);
dropdown.selectItemByIndex((int) value); dropdown.setOption((int) value);
} }
public void nullify(){ public void nullify(){
isBlank = true; isBlank = true;
@@ -132,7 +102,7 @@ public class dropdownType extends inputType {
public dataType getViewValue(){ public dataType getViewValue(){
if(dropdown == null) return null; if(dropdown == null) return null;
if(dropdown.getVisibility() == View.GONE) return new intType(name, intType.nullval); if(dropdown.getVisibility() == View.GONE) return new intType(name, intType.nullval);
return new intType(name, dropdown.getSelectedIndex()); return new intType(name, dropdown.getIndex());
} }
@@ -275,5 +245,9 @@ public class dropdownType extends inputType {
chart.invalidate(); chart.invalidate();
parent.addView(chart); parent.addView(chart);
} }
public String toString(dataType data){
return text_options[(int) data.get()];
}
} }
@@ -2,6 +2,7 @@ package com.ridgebotics.ridgescout.types.input;
import static android.text.InputType.TYPE_CLASS_NUMBER; import static android.text.InputType.TYPE_CLASS_NUMBER;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.graphics.Color; import android.graphics.Color;
import android.text.Editable; import android.text.Editable;
@@ -39,8 +40,8 @@ public class fieldposType extends inputType {
public Object get_fallback_value(){return 0;} public Object get_fallback_value(){return 0;}
public fieldposType(){} public fieldposType(){}
public String get_type_name(){return "Field Pos";} public String get_type_name(){return "Field Pos";}
public fieldposType(String name, int[] default_value){ public fieldposType(String name, String description, int[] default_value){
super(name); super(name, description);
this.default_value = default_value; this.default_value = default_value;
} }
@@ -51,6 +52,7 @@ public class fieldposType extends inputType {
public byte[] encode() throws ByteBuilder.buildingException { public byte[] encode() throws ByteBuilder.buildingException {
ByteBuilder bb = new ByteBuilder(); ByteBuilder bb = new ByteBuilder();
bb.addString(name); bb.addString(name);
bb.addString(description);
bb.addIntArray((int[]) default_value); bb.addIntArray((int[]) default_value);
return bb.build(); return bb.build();
} }
@@ -60,8 +62,8 @@ public class fieldposType extends inputType {
ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse(); ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse();
name = (String) objects.get(0).get(); name = (String) objects.get(0).get();
default_value = objects.get(1).get(); description = (String) objects.get(1).get();
System.out.println("Defalt value!!!!!" + default_value); default_value = objects.get(2).get();
} }
@@ -85,6 +87,10 @@ public class fieldposType extends inputType {
nullify(); nullify();
return; return;
} }
if(((int[]) value)[0] == 255 && ((int[]) value)[1] == 255){
nullify();
return;
}
isBlank = false; isBlank = false;
field.setVisibility(View.VISIBLE); field.setVisibility(View.VISIBLE);
@@ -226,5 +232,10 @@ public class fieldposType extends inputType {
parent.addView(chart); parent.addView(chart);
} }
public String toString(dataType data){
int[] intarr = (int[]) data.get();
return "[" + intarr[0] + "," + intarr[1] + "]";
}
} }
@@ -1,5 +1,6 @@
package com.ridgebotics.ridgescout.types.input; package com.ridgebotics.ridgescout.types.input;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.view.View; import android.view.View;
import android.widget.LinearLayout; import android.widget.LinearLayout;
@@ -29,14 +30,16 @@ public abstract class inputType {
FIELDPOS FIELDPOS
} }
public String name; public String name;
public String description;
public Object default_value; public Object default_value;
public abstract inputTypes getInputType(); public abstract inputTypes getInputType();
public abstract dataType.valueTypes getValueType(); public abstract dataType.valueTypes getValueType();
public abstract Object get_fallback_value(); public abstract Object get_fallback_value();
public abstract int get_byte_id(); public abstract int get_byte_id();
public inputType(){} public inputType(){}
public inputType(String name){ public inputType(String name, String description){
this.name = name; this.name = name;
this.description = description;
} }
public abstract String get_type_name(); public abstract String get_type_name();
@@ -104,4 +107,7 @@ public abstract class inputType {
public abstract void add_history_view(LinearLayout parent, dataType[] data); public abstract void add_history_view(LinearLayout parent, dataType[] data);
public abstract String toString(dataType data);
} }
@@ -2,6 +2,7 @@ package com.ridgebotics.ridgescout.types.input;
import static android.text.InputType.TYPE_CLASS_NUMBER; import static android.text.InputType.TYPE_CLASS_NUMBER;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.graphics.Color; import android.graphics.Color;
import android.text.Editable; import android.text.Editable;
@@ -35,8 +36,8 @@ public class numberType extends inputType {
public Object get_fallback_value(){return 0;} public Object get_fallback_value(){return 0;}
public numberType(){} public numberType(){}
public String get_type_name(){return "Number";} public String get_type_name(){return "Number";}
public numberType(String name, int default_value){ public numberType(String name, String description, int default_value){
super(name); super(name, description);
this.default_value = default_value; this.default_value = default_value;
} }
@@ -47,6 +48,7 @@ public class numberType extends inputType {
public byte[] encode() throws ByteBuilder.buildingException { public byte[] encode() throws ByteBuilder.buildingException {
ByteBuilder bb = new ByteBuilder(); ByteBuilder bb = new ByteBuilder();
bb.addString(name); bb.addString(name);
bb.addString(description);
bb.addInt((int)default_value); bb.addInt((int)default_value);
return bb.build(); return bb.build();
} }
@@ -55,7 +57,8 @@ public class numberType extends inputType {
ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse(); ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse();
name = (String) objects.get(0).get(); name = (String) objects.get(0).get();
default_value = objects.get(1).get(); description = (String) objects.get(1).get();
default_value = objects.get(2).get();
} }
@@ -315,5 +318,9 @@ public class numberType extends inputType {
parent.addView(chart); parent.addView(chart);
} }
public String toString(dataType data){
return String.valueOf((int) data.get());
}
} }
@@ -1,5 +1,6 @@
package com.ridgebotics.ridgescout.types.input; package com.ridgebotics.ridgescout.types.input;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.graphics.Color; import android.graphics.Color;
import android.view.View; import android.view.View;
@@ -35,8 +36,8 @@ public class sliderType extends inputType {
public Object get_fallback_value(){return 0;} public Object get_fallback_value(){return 0;}
public sliderType(){}; public sliderType(){};
public String get_type_name(){return "Slider";} public String get_type_name(){return "Slider";}
public sliderType(String name, int defaultValue, int min, int max){ public sliderType(String name, String description, int defaultValue, int min, int max){
super(name); super(name, description);
this.default_value = defaultValue; this.default_value = defaultValue;
this.min = min; this.min = min;
this.max = max; this.max = max;
@@ -48,6 +49,7 @@ public class sliderType extends inputType {
public byte[] encode() throws ByteBuilder.buildingException { public byte[] encode() throws ByteBuilder.buildingException {
ByteBuilder bb = new ByteBuilder(); ByteBuilder bb = new ByteBuilder();
bb.addString(name); bb.addString(name);
bb.addString(description);
bb.addInt((int)default_value); bb.addInt((int)default_value);
bb.addInt(min); bb.addInt(min);
bb.addInt(max); bb.addInt(max);
@@ -58,9 +60,10 @@ public class sliderType extends inputType {
ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse(); ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse();
name = (String) objects.get(0).get(); name = (String) objects.get(0).get();
default_value = objects.get(1).get(); description = (String) objects.get(1).get();
min = (int) objects.get(2).get(); default_value = objects.get(2).get();
max = (int) objects.get(3).get(); min = (int) objects.get(3).get();
max = (int) objects.get(4).get();
} }
@@ -302,4 +305,8 @@ public class sliderType extends inputType {
parent.addView(chart); parent.addView(chart);
} }
public String toString(dataType data){
return String.valueOf((int) data.get());
}
} }
@@ -1,5 +1,6 @@
package com.ridgebotics.ridgescout.types.input; package com.ridgebotics.ridgescout.types.input;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.graphics.Color; import android.graphics.Color;
import android.view.Gravity; import android.view.Gravity;
@@ -31,8 +32,8 @@ public class tallyType extends inputType {
public Object get_fallback_value(){return 0;} public Object get_fallback_value(){return 0;}
public tallyType(){} public tallyType(){}
public String get_type_name(){return "Tally";} public String get_type_name(){return "Tally";}
public tallyType(String name, int default_value){ public tallyType(String name, String description, int default_value){
super(name); super(name, description);
this.default_value = default_value; this.default_value = default_value;
} }
@@ -43,6 +44,7 @@ public class tallyType extends inputType {
public byte[] encode() throws ByteBuilder.buildingException { public byte[] encode() throws ByteBuilder.buildingException {
ByteBuilder bb = new ByteBuilder(); ByteBuilder bb = new ByteBuilder();
bb.addString(name); bb.addString(name);
bb.addString(description);
bb.addInt((int)default_value); bb.addInt((int)default_value);
return bb.build(); return bb.build();
} }
@@ -51,7 +53,8 @@ public class tallyType extends inputType {
ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse(); ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse();
name = (String) objects.get(0).get(); name = (String) objects.get(0).get();
default_value = objects.get(1).get(); description = (String) objects.get(1).get();
default_value = objects.get(2).get();
} }
@@ -72,7 +75,6 @@ public class tallyType extends inputType {
public void setViewValue(Object value) { public void setViewValue(Object value) {
if(tally == null) return; if(tally == null) return;
System.out.println(value);
if(intType.isNull((int)value)){ if(intType.isNull((int)value)){
nullify(); nullify();
return; return;
@@ -296,5 +298,9 @@ public class tallyType extends inputType {
parent.addView(chart); parent.addView(chart);
} }
public String toString(dataType data){
return String.valueOf((int) data.get());
}
} }
@@ -1,5 +1,6 @@
package com.ridgebotics.ridgescout.types.input; package com.ridgebotics.ridgescout.types.input;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.graphics.Color; import android.graphics.Color;
import android.text.Editable; import android.text.Editable;
@@ -34,8 +35,8 @@ public class textType extends inputType {
public dataType.valueTypes getValueType(){return dataType.valueTypes.STRING;} public dataType.valueTypes getValueType(){return dataType.valueTypes.STRING;}
public Object get_fallback_value(){return "<no-notes>";} public Object get_fallback_value(){return "<no-notes>";}
public textType(){} public textType(){}
public textType(String name, String default_text){ public textType(String name, String description, String default_text){
super(name); super(name, description);
this.default_value = default_text; this.default_value = default_text;
} }
public String get_type_name(){return "Text";} public String get_type_name(){return "Text";}
@@ -49,6 +50,7 @@ public class textType extends inputType {
public byte[] encode() throws ByteBuilder.buildingException { public byte[] encode() throws ByteBuilder.buildingException {
ByteBuilder bb = new ByteBuilder(); ByteBuilder bb = new ByteBuilder();
bb.addString(name); bb.addString(name);
bb.addString(description);
bb.addString((String) default_value); bb.addString((String) default_value);
return bb.build(); return bb.build();
} }
@@ -57,7 +59,8 @@ public class textType extends inputType {
ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse(); ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse();
name = (String) objects.get(0).get(); name = (String) objects.get(0).get();
default_value = objects.get(1).get(); description = (String) objects.get(1).get();
default_value = objects.get(2).get();
} }
@@ -227,5 +230,9 @@ public class textType extends inputType {
parent.addView(chart); parent.addView(chart);
} }
public String toString(dataType data){
return String.valueOf(data.get());
}
} }
@@ -0,0 +1,208 @@
package com.ridgebotics.ridgescout.ui;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewTreeObserver;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
public class BackgroundView extends View {
private List<Circle> circles;
private Paint whitePaint;
private Paint greenPaint;
private Random random;
// Physics simulation constants
private static final float GRAVITY = 9.8f;
private static final float DAMPING = 0.5f;
private static final float CIRCLE_SPAWN_INTERVAL = 500; // milliseconds
private long lastSpawnTime = 0;
// Screen dimensions
private int screenWidth;
private int screenHeight;
public BackgroundView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}
private void initialize() {
// Setup paints
whitePaint = new Paint();
whitePaint.setColor(Color.WHITE);
whitePaint.setStyle(Paint.Style.STROKE);
whitePaint.setStrokeWidth(2);
greenPaint = new Paint();
greenPaint.setColor(Color.GREEN);
greenPaint.setStyle(Paint.Style.FILL);
circles = new ArrayList<>();
random = new Random();
// Get screen dimensions after layout
ViewTreeObserver vto = getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
screenWidth = getWidth();
screenHeight = getHeight();
getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Spawn new circles periodically
long currentTime = System.currentTimeMillis();
if (currentTime - lastSpawnTime > CIRCLE_SPAWN_INTERVAL) {
spawnCircle();
lastSpawnTime = currentTime;
}
// First pass: detect all collisions
List<Collision> collisions = new ArrayList<>();
for (int i = 0; i < circles.size(); i++) {
Circle circle = circles.get(i);
// Wall and floor collisions
if (circle.x - circle.radius < 0) {
circle.x = circle.radius;
circle.velocityX *= -DAMPING;
}
if (circle.x + circle.radius > screenWidth) {
circle.x = screenWidth - circle.radius;
circle.velocityX *= -DAMPING;
}
if (circle.y + circle.radius > screenHeight) {
circle.y = screenHeight - circle.radius;
circle.velocityY = 0;
}
// Detect collisions with other circles
for (int j = i + 1; j < circles.size(); j++) {
Circle otherCircle = circles.get(j);
float dx = otherCircle.x - circle.x;
float dy = otherCircle.y - circle.y;
float distance = (float) Math.sqrt(dx * dx + dy * dy);
if (distance < circle.radius + otherCircle.radius) {
collisions.add(new Collision(circle, otherCircle, dx, dy, distance));
}
}
// Apply gravity
circle.velocityY += GRAVITY * 0.1f;
}
// Second pass: resolve collisions
for (Collision collision : collisions) {
Circle c1 = collision.circle1;
Circle c2 = collision.circle2;
float dx = collision.dx;
float dy = collision.dy;
float distance = collision.distance;
// Calculate overlap
float overlap = (c1.radius + c2.radius - distance) / 2;
// Separate circles
float separationX = overlap * dx / distance;
float separationY = overlap * dy / distance;
c1.x -= separationX;
c1.y -= separationY;
c2.x += separationX;
c2.y += separationY;
// Dampen velocities
c1.velocityX *= DAMPING;
c1.velocityY *= DAMPING;
c2.velocityX *= DAMPING;
c2.velocityY *= DAMPING;
}
// Draw and update circles
Iterator<Circle> iterator = circles.iterator();
while (iterator.hasNext()) {
Circle circle = iterator.next();
// Update position
circle.x += circle.velocityX;
circle.y += circle.velocityY;
// Draw circle
Paint paint = circle.isGreen ? greenPaint : whitePaint;
canvas.drawCircle(circle.x, circle.y, circle.radius, paint);
// Remove circles that have fallen off screen
if (circle.y > screenHeight + circle.radius) {
iterator.remove();
}
}
// Trigger redraw
invalidate();
}
// Collision tracking class
private static class Collision {
Circle circle1, circle2;
float dx, dy, distance;
Collision(Circle c1, Circle c2, float dx, float dy, float distance) {
this.circle1 = c1;
this.circle2 = c2;
this.dx = dx;
this.dy = dy;
this.distance = distance;
}
}
private void spawnCircle() {
// More likely to spawn white circles
boolean isGreen = random.nextFloat() < 0.2f;
float radius = isGreen ?
120: // Green: 20-60
80; // White: 10-30
float x = random.nextFloat() * screenWidth;
float velocityX = (random.nextFloat() - 0.5f) * 5;
circles.add(new Circle(x, -radius, radius, velocityX, 0, isGreen));
}
// Circle class to represent individual circles
private static class Circle {
float x, y;
float radius;
float velocityX, velocityY;
boolean isGreen;
Circle(float x, float y, float radius, float velocityX, float velocityY, boolean isGreen) {
this.x = x;
this.y = y;
this.radius = radius;
this.velocityX = velocityX;
this.velocityY = velocityY;
this.isGreen = isGreen;
}
}
}
@@ -0,0 +1,117 @@
package com.ridgebotics.ridgescout.ui;
import android.content.Context;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.LinearLayout;
import android.widget.TableLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.appbar.MaterialToolbar;
import com.google.android.material.divider.MaterialDivider;
import com.ridgebotics.ridgescout.R;
import java.util.ArrayList;
import java.util.List;
public class CustomSpinnerPopup extends TableLayout {
public CustomSpinnerPopup(Context context) {
super(context);
}
public CustomSpinnerPopup(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public interface OnOptionSelectedListener {
void onOptionSelected(String option);
}
public CustomSpinnerPopup init(List<String> options, OnOptionSelectedListener onOptionSelectedListener, int defaultOption){
CheckBox[] checkBoxes = new CheckBox[options.size()];
setPadding(16, 16, 16, 16);
for(int i = 0; i < options.size(); i++){
final CheckBox cb = new CheckBox(getContext());
cb.setText(options.get(i));
cb.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Headline5);
cb.setChecked(i == defaultOption);
if(i > 0)
addView(new MaterialDivider(getContext()));
addView(cb);
checkBoxes[i] = cb;
final int fi = i;
cb.setOnClickListener(a -> {
onOptionSelectedListener.onOptionSelected(options.get(fi));
for (CheckBox checkBox : checkBoxes)
checkBox.setChecked(false);
cb.setChecked(true);
});
}
return this;
}
// public static CustomSpinnerPopup newInstance(ArrayList<String> options) {
// CustomSpinnerPopup dialog = new CustomSpinnerPopup();
// Bundle args = new Bundle();
// args.putStringArrayList("options", options);
//
//
//
//// dialog.setArguments(args);
// return dialog;
// }
// @Override
// public void onCreate(Bundle savedInstanceState) {
// super.onCreate(savedInstanceState);
// setStyle(DialogFragment.STYLE_NORMAL, R.style.FullScreenDialogStyle);
// if (getArguments() != null) {
// options = getArguments().getStringArrayList("options");
// }
// }
//
// @Nullable
// @Override
// public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
// @Nullable Bundle savedInstanceState) {
// View view = inflater.inflate(R.layout.view_custom_spinner_popup, container, false);
//
// // Setup toolbar
// MaterialToolbar toolbar = view.findViewById(R.id.toolbar);
// toolbar.setNavigationOnClickListener(v -> dismiss());
// toolbar.setTitle("Select an Option");
//
// // Setup RecyclerView
// recyclerView = view.findViewById(R.id.recyclerView);
// recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
// adapter = new CustomSpinnerOptionsAdapter(options, option -> {
// if (listener != null) {
// listener.onOptionSelected(option);
// }
// dismiss();
// });
// recyclerView.setAdapter(adapter);
//
// return view;
// }
//
}
@@ -0,0 +1,130 @@
package com.ridgebotics.ridgescout.ui;
import static android.app.PendingIntent.getActivity;
import static com.ridgebotics.ridgescout.utility.settingsManager.getEditor;
import android.app.AlertDialog;
import android.content.Context;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentManager;
import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.databinding.ViewCustomSpinnerBinding;
import java.util.ArrayList;
import java.util.List;
public class CustomSpinnerView extends LinearLayout {
public interface onClickListener {
void onClick(String item, int index);
}
public CustomSpinnerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public CustomSpinnerView(Context context) {
super(context);
init(context);
}
private List<String> options;
private onClickListener onClickListener;
private TextView title;
private TextView item;
private int index = -1;
public void init(Context context) {
LayoutInflater.from(context).inflate(R.layout.view_custom_spinner, this, true);
title = findViewById(R.id.title);
item = findViewById(R.id.item);
}
public void setOnClickListener(onClickListener listener){
this.onClickListener = listener;
}
public void setOptions(List<String> options, String defaultOption){
setOptions(options, options.indexOf(defaultOption));
}
public void setOptions(List<String> options, int defaultOption){
this.options = options;
this.index = defaultOption;
if(defaultOption != -1)
this.item.setText(options.get(defaultOption));
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
ScrollView sv = new ScrollView(getContext());
// sv.setLayoutDirection(ScrollView.SCROLL_AXIS_VERTICAL);
LinearLayout ll = new LinearLayout(getContext());
ll.setOrientation(LinearLayout.VERTICAL);
sv.addView(ll);
builder.setPositiveButton("OK", (dialog, which) -> {});
CustomSpinnerPopup popup = new CustomSpinnerPopup(getContext()).init(options, option -> {
// dialog.();
if(!isEnabled()) return;
item.setText(option);
index = options.indexOf(option);
if(onClickListener != null) {
onClickListener.onClick(option, options.indexOf(option));
}
}, index);
ll.addView(popup);
// popup.setLayoutDirection(0);
builder.setView(sv);
AlertDialog dialog = builder.create();
// popup.setOnOptionSelectedListener();
this.setOnClickListener(v -> {
if(!isEnabled()) return;
dialog.show();
});
}
public void setTitle(String text){
title.setText(text);
}
public void setOption(String option) {
item.setText(option);
index = options.indexOf(option);
}
public void setOption(int index) {
item.setText(options.get(index));
this.index = index;
}
public int getIndex(){
return index;
}
public String getOption(){
return options.get(index);
}
}
@@ -0,0 +1,96 @@
package com.ridgebotics.ridgescout.ui;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.ridgebotics.ridgescout.R;
public class ToggleTitleView extends ConstraintLayout {
public ToggleTitleView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public ToggleTitleView(@NonNull Context context) {
super(context);
init(context);
}
public interface OnToggleListener {
void onToggle(boolean enabled);
}
TextView titleView;
CheckBox toggle_title_checkbox;
TextView toggle_title_description;
OnToggleListener onToggleListener;
public void init(Context context){
LayoutInflater.from(context).inflate(R.layout.view_toggle_title, this, true);
titleView = findViewById(R.id.toggle_title);
toggle_title_checkbox = findViewById(R.id.toggle_title_checkbox);
toggle_title_description = findViewById(R.id.toggle_title_description);
toggle_title_checkbox.setOnCheckedChangeListener((compoundButton, checked) -> {
// If checkbox has already updated
if(enabled == checked) return;
if (checked)
enable();
else
disable();
onToggleListener.onToggle(!checked);
});
}
public void setTitle(String title){
titleView.setText(title);
}
public void setDescription(String description){
toggle_title_description.setText(description);
}
public void setOnToggleListener(OnToggleListener onToggleListener){
this.onToggleListener = onToggleListener;
}
public boolean enabled = true;
public boolean isEnabled(){return enabled;}
public void disable(){
enabled = false;
toggle_title_checkbox.setChecked(false);
toggle_title_description.setVisibility(View.GONE);
setBackgroundColor(0xffff0000);
titleView.setTextColor(0xff000000);
}
public void enable(){
enabled = true;
toggle_title_checkbox.setChecked(true);
toggle_title_description.setVisibility(View.VISIBLE);
setBackgroundColor(0x00000000);
titleView.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Headline5);
}
public void setEnabled(boolean enabled){
if(enabled)
disable();
else
enable();
}
}
@@ -1,23 +0,0 @@
package com.ridgebotics.ridgescout.ui.data;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.databinding.FragmentDataCompareBinding;
public class CompareFragment extends Fragment {
FragmentDataCompareBinding binding;
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentDataCompareBinding.inflate(inflater, container, false);
return binding.getRoot();
}
}
@@ -1,6 +1,7 @@
package com.ridgebotics.ridgescout.ui.data; package com.ridgebotics.ridgescout.ui.data;
import static android.view.View.VISIBLE;
import static androidx.navigation.fragment.FragmentKt.findNavController; import static androidx.navigation.fragment.FragmentKt.findNavController;
import android.os.Bundle; import android.os.Bundle;
@@ -14,6 +15,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.scoutingData.fields;
import com.ridgebotics.ridgescout.utility.settingsManager; 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;
@@ -36,17 +38,26 @@ public class DataFragment extends Fragment {
String evcode = settingsManager.getEVCode(); 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); binding.fieldsButton.setEnabled(false);
binding.fieldsButtons.setVisibility(VISIBLE);
});
binding.fieldsMatchesButton.setOnClickListener(v -> {
FieldsFragment.set_filename(fields.matchFieldsFilename);
findNavController(this).navigate(R.id.action_navigation_data_to_navigation_data_fields);
});
binding.fieldsPitsButton.setOnClickListener(v -> {
FieldsFragment.set_filename(fields.pitsFieldsFilename);
findNavController(this).navigate(R.id.action_navigation_data_to_navigation_data_fields);
}); });
if(evcode.equals("unset")){ if(evcode.equals("unset")){
binding.noEventError.setVisibility(View.VISIBLE); binding.noEventError.setVisibility(VISIBLE);
binding.buttons.setVisibility(View.VISIBLE); binding.buttons.setVisibility(VISIBLE);
binding.teamsButton.setEnabled(false); binding.teamsButton.setEnabled(false);
binding.compareButton.setEnabled(false); binding.fieldsButton.setVisibility(VISIBLE);
binding.reportButton.setEnabled(false);
binding.fieldsButton.setVisibility(View.VISIBLE);
return root; return root;
@@ -56,24 +67,12 @@ public class DataFragment extends Fragment {
binding.teamsButton.setOnClickListener(v -> { binding.teamsButton.setOnClickListener(v -> {
TeamSelectorFragment.setPits_mode(false); TeamSelectorFragment.setPits_mode(false);
TeamSelectorFragment.setOnSelect(new TeamSelectorFragment.onTeamSelected() { TeamSelectorFragment.setOnSelect((self, team) -> {
@Override
public void onSelect(TeamSelectorFragment self, frcTeam team) {
TeamsFragment.setTeam(team); TeamsFragment.setTeam(team);
findNavController(self).navigate(R.id.action_navigation_team_selector_to_navigation_data_teams); findNavController(self).navigate(R.id.action_navigation_team_selector_to_navigation_data_teams);
}
}); });
findNavController(this).navigate(R.id.action_navigation_data_to_navigation_team_selector); findNavController(this).navigate(R.id.action_navigation_data_to_navigation_team_selector);
}); });
binding.compareButton.setOnClickListener(v -> {
findNavController(this).navigate(R.id.action_navigation_data_to_navigation_data_compare);
});
binding.reportButton.setOnClickListener(v -> {
findNavController(this).navigate(R.id.action_navigation_data_to_navigation_data_report_selector);
});
return root; return root;
} }
} }
@@ -68,27 +68,34 @@ public class FieldEditorHelper {
// } // }
public static final parameterType[] defaultSliderParams = new parameterType[]{ public static final parameterType[] defaultSliderParams = new parameterType[]{
new paramString("Description", ""),
new paramNumber("Min", 0), new paramNumber("Min", 0),
new paramNumber("Max", 10), new paramNumber("Max", 10),
new paramNumber("Default Value", 5) new paramNumber("Default Value", 5)
}; };
public static final parameterType[] defaultDropdownParams = new parameterType[]{ public static final parameterType[] defaultDropdownParams = new parameterType[]{
new paramString("Description", ""),
new paramStringArray("Default Value", new String[]{"Zero","One","Two","Three"}), new paramStringArray("Default Value", new String[]{"Zero","One","Two","Three"}),
new paramNumber("Default Option", 0), new paramNumber("Default Option", 0),
}; };
public static final parameterType[] defaultTextParams = new parameterType[]{ public static final parameterType[] defaultTextParams = new parameterType[]{
new paramString("Description", ""),
new paramString("Default Value", "") new paramString("Default Value", "")
}; };
public static final parameterType[] defaultTallyParams = new parameterType[]{ public static final parameterType[] defaultTallyParams = new parameterType[]{
new paramString("Description", ""),
new paramNumber("Default Value", 0) new paramNumber("Default Value", 0)
}; };
public static final parameterType[] defaultNumberParams = new parameterType[]{ public static final parameterType[] defaultNumberParams = new parameterType[]{
new paramString("Description", ""),
new paramNumber("Default Value", 0) new paramNumber("Default Value", 0)
}; };
public static final parameterType[] defaultCheckboxParam = new parameterType[]{ public static final parameterType[] defaultCheckboxParam = new parameterType[]{
new paramString("Description", ""),
new paramNumber("Default Value ( 1 or 0 )", 0) new paramNumber("Default Value ( 1 or 0 )", 0)
}; };
public static final parameterType[] defaultFieldPosParam = new parameterType[]{ public static final parameterType[] defaultFieldPosParam = new parameterType[]{
new paramString("Description", ""),
new paramNumber("Default X", 0), new paramNumber("Default X", 0),
new paramNumber("Default Y", 0) new paramNumber("Default Y", 0)
}; };
@@ -96,6 +103,7 @@ public class FieldEditorHelper {
private static parameterType[] getSliderParams(sliderType s){ private static parameterType[] getSliderParams(sliderType s){
return new parameterType[]{ return new parameterType[]{
new paramString("Description", s.description),
new paramNumber("Min", s.min), new paramNumber("Min", s.min),
new paramNumber("Max", s.max), new paramNumber("Max", s.max),
new paramNumber("Default Value", (int) s.default_value) new paramNumber("Default Value", (int) s.default_value)
@@ -104,6 +112,7 @@ public class FieldEditorHelper {
private static parameterType[] getDropdownParams(dropdownType s){ private static parameterType[] getDropdownParams(dropdownType s){
return new parameterType[]{ return new parameterType[]{
new paramString("Description", s.description),
new paramStringArray("Default Value",s.text_options), new paramStringArray("Default Value",s.text_options),
new paramNumber("Default Option", (int) s.default_value), new paramNumber("Default Option", (int) s.default_value),
}; };
@@ -111,30 +120,35 @@ public class FieldEditorHelper {
private static parameterType[] getTextParams(textType s){ private static parameterType[] getTextParams(textType s){
return new parameterType[]{ return new parameterType[]{
new paramString("Description", s.description),
new paramString("Default Value", (String) s.default_value) new paramString("Default Value", (String) s.default_value)
}; };
} }
private static parameterType[] getTallyParams(tallyType s){ private static parameterType[] getTallyParams(tallyType s){
return new parameterType[]{ return new parameterType[]{
new paramString("Description", s.description),
new paramNumber("Default Value", (int) s.default_value) new paramNumber("Default Value", (int) s.default_value)
}; };
} }
private static parameterType[] getNumberParams(numberType s){ private static parameterType[] getNumberParams(numberType s){
return new parameterType[]{ return new parameterType[]{
new paramString("Description", s.description),
new paramNumber("Default Value", (int) s.default_value) new paramNumber("Default Value", (int) s.default_value)
}; };
} }
private static parameterType[] getCheckboxParam(checkboxType s){ private static parameterType[] getCheckboxParam(checkboxType s){
return new parameterType[]{ return new parameterType[]{
new paramString("Description", s.description),
new paramNumber("Default Value ( 1 or 0 )", (int) s.default_value) new paramNumber("Default Value ( 1 or 0 )", (int) s.default_value)
}; };
} }
private static parameterType[] getFieldPosParam(fieldposType s){ private static parameterType[] getFieldPosParam(fieldposType s){
return new parameterType[]{ return new parameterType[]{
new paramString("Description", s.description),
new paramNumber("Default X", ((int[]) s.default_value)[0]), new paramNumber("Default X", ((int[]) s.default_value)[0]),
new paramNumber("Default Y", ((int[]) s.default_value)[1]) new paramNumber("Default Y", ((int[]) s.default_value)[1])
}; };
@@ -143,36 +157,43 @@ public class FieldEditorHelper {
public static void setSliderParams(sliderType s, parameterType[] types){ public static void setSliderParams(sliderType s, parameterType[] types){
s.min = ((paramNumber) types[0]).val; s.description = ((paramString) types[0]).val;
s.max = ((paramNumber) types[1]).val; s.min = ((paramNumber) types[1]).val;
s.default_value = ((paramNumber) types[2]).val; s.max = ((paramNumber) types[2]).val;
s.default_value = ((paramNumber) types[3]).val;
} }
public static void setDropdownParams(dropdownType s, parameterType[] types){ public static void setDropdownParams(dropdownType s, parameterType[] types){
s.text_options = ((paramStringArray) types[0]).val; s.description = ((paramString) types[0]).val;
s.default_value = ((paramNumber) types[1]).val; s.text_options = ((paramStringArray) types[1]).val;
s.default_value = ((paramNumber) types[2]).val;
} }
public static void setTextParams(textType s, parameterType[] types){ public static void setTextParams(textType s, parameterType[] types){
s.default_value = ((paramString) types[0]).val; s.description = ((paramString) types[0]).val;
s.default_value = ((paramString) types[1]).val;
} }
public static void setTallyParams(tallyType s, parameterType[] types){ public static void setTallyParams(tallyType s, parameterType[] types){
s.default_value = ((paramNumber) types[0]).val; s.description = ((paramString) types[0]).val;
s.default_value = ((paramNumber) types[1]).val;
} }
public static void setNumberParams(numberType s, parameterType[] types){ public static void setNumberParams(numberType s, parameterType[] types){
s.default_value = ((paramNumber) types[0]).val; s.description = ((paramString) types[0]).val;
s.default_value = ((paramNumber) types[1]).val;
} }
public static void setCheckboxParam(checkboxType s, parameterType[] types){ public static void setCheckboxParam(checkboxType s, parameterType[] types){
s.default_value = ((paramNumber) types[0]).val; s.description = ((paramString) types[0]).val;
s.default_value = ((paramNumber) types[1]).val;
} }
public static void setFieldPosParam(fieldposType s, parameterType[] types){ public static void setFieldPosParam(fieldposType s, parameterType[] types){
s.description = ((paramString) types[0]).val;
s.default_value = new int[]{ s.default_value = new int[]{
((paramNumber) types[0]).val, ((paramNumber) types[1]).val,
((paramNumber) types[1]).val ((paramNumber) types[2]).val
}; };
} }
@@ -1,38 +0,0 @@
package com.ridgebotics.ridgescout.ui.data;
import static androidx.navigation.fragment.FragmentKt.findNavController;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.databinding.FragmentDataFieldsChooserBinding;
import com.ridgebotics.ridgescout.scoutingData.fields;
public class FieldsChooserFragment extends Fragment {
FragmentDataFieldsChooserBinding binding;
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentDataFieldsChooserBinding.inflate(inflater, container, false);
binding.matchScoutingButton.setOnClickListener(v -> {
FieldsFragment.set_filename(fields.matchFieldsFilename);
findNavController(this).navigate(R.id.action_navigation_data_fields_chooser_to_navigation_data_fields);
});
binding.pitScoutingButton.setOnClickListener(v -> {
FieldsFragment.set_filename(fields.pitsFieldsFilename);
findNavController(this).navigate(R.id.action_navigation_data_fields_chooser_to_navigation_data_fields);
});
return binding.getRoot();
}
}
@@ -21,6 +21,7 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation; import androidx.navigation.Navigation;
import com.ridgebotics.ridgescout.MainActivity;
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;
@@ -32,11 +33,8 @@ import com.ridgebotics.ridgescout.types.input.numberType;
import com.ridgebotics.ridgescout.types.input.sliderType; import com.ridgebotics.ridgescout.types.input.sliderType;
import com.ridgebotics.ridgescout.types.input.tallyType; import com.ridgebotics.ridgescout.types.input.tallyType;
import com.ridgebotics.ridgescout.types.input.textType; import com.ridgebotics.ridgescout.types.input.textType;
import com.ridgebotics.ridgescout.ui.CustomSpinnerView;
import com.ridgebotics.ridgescout.utility.AlertManager; import com.ridgebotics.ridgescout.utility.AlertManager;
import com.skydoves.powerspinner.IconSpinnerAdapter;
import com.skydoves.powerspinner.IconSpinnerItem;
import com.skydoves.powerspinner.PowerSpinnerView;
import com.skydoves.powerspinner.SpinnerGravity;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@@ -55,10 +53,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);
@@ -71,6 +67,25 @@ public class FieldsFragment extends Fragment {
load_field_menu(); load_field_menu();
((MainActivity) getActivity()).setOnBackPressed(() -> {
if(binding.saveButton.getVisibility() == View.GONE) return true;
AlertDialog.Builder alert = new AlertDialog.Builder(getContext());
alert.setTitle("Warning!");
alert.setMessage("You have not saved your progress!");
alert.setPositiveButton("Return", null);
alert.setNeutralButton("Quit without saving", (dialogInterface, i) -> {
binding.saveButton.setVisibility(View.GONE);
if(getActivity() != null)
getActivity().onBackPressed();
});
alert.setCancelable(true);
alert.create().show();
return false;
});
return binding.getRoot(); return binding.getRoot();
} }
@@ -124,6 +139,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) {
@@ -138,6 +172,8 @@ public class FieldsFragment extends Fragment {
binding.addButton.setVisibility(View.VISIBLE); binding.addButton.setVisibility(View.VISIBLE);
binding.downButton.setVisibility(View.VISIBLE); binding.downButton.setVisibility(View.VISIBLE);
updateUpDownAvailability();
binding.valueEditContainer.setVisibility(View.GONE); binding.valueEditContainer.setVisibility(View.GONE);
for(int i = 0; i < version_values.length; i++){ for(int i = 0; i < version_values.length; i++){
@@ -150,6 +186,12 @@ public class FieldsFragment extends Fragment {
binding.saveButton.setOnClickListener(this::buttonfunc); binding.saveButton.setOnClickListener(this::buttonfunc);
} }
// Make sure the user cannot move fields when they shouldn't
private void updateUpDownAvailability(){
binding.upButton.setEnabled(selindex != -1 && selindex != 0);
binding.downButton.setEnabled(selindex != -1 && selindex != values[values.length-1].length-1);
}
private void addRow(inputType field){ private void addRow(inputType field){
TableRow tr = getTableRow(field); TableRow tr = getTableRow(field);
@@ -158,6 +200,7 @@ public class FieldsFragment extends Fragment {
tr.setOnClickListener(v -> { tr.setOnClickListener(v -> {
binding.editButton.setVisibility(View.VISIBLE); binding.editButton.setVisibility(View.VISIBLE);
trOnClick(values[values.length-1], tr); trOnClick(values[values.length-1], tr);
updateUpDownAvailability();
}); });
binding.upButton.setOnClickListener(v -> { binding.upButton.setOnClickListener(v -> {
@@ -166,6 +209,7 @@ public class FieldsFragment extends Fragment {
binding.fieldsArea.updateRowOrder(selindex, selindex - 1); binding.fieldsArea.updateRowOrder(selindex, selindex - 1);
selindex -= 1; selindex -= 1;
} }
updateUpDownAvailability();
}); });
binding.downButton.setOnClickListener(v -> { binding.downButton.setOnClickListener(v -> {
@@ -174,6 +218,7 @@ public class FieldsFragment extends Fragment {
binding.fieldsArea.updateRowOrder(selindex, selindex + 1); binding.fieldsArea.updateRowOrder(selindex, selindex + 1);
selindex += 1; selindex += 1;
} }
updateUpDownAvailability();
}); });
} }
@@ -182,10 +227,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][];
@@ -198,11 +240,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);
}); });
alert.setNegativeButton("Cancel", null);
alert.setCancelable(true);
alert.create().show(); alert.create().show();
} }
@@ -374,53 +418,26 @@ public class FieldsFragment extends Fragment {
private void addField_Part_2(String title) { private void addField_Part_2(String title) {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle("Title"); builder.setTitle("Select Type");
final PowerSpinnerView dropdown = new PowerSpinnerView(getContext()); final CustomSpinnerView dropdown = new CustomSpinnerView(getContext());
List<String> options = new ArrayList<>();
List<IconSpinnerItem> iconSpinnerItems = new ArrayList<>(); options.add("Slider");
options.add("Text");
options.add("Dropdown");
options.add("Tally");
options.add("Number");
options.add("Checkbox");
options.add("Field Position");
iconSpinnerItems.add(new IconSpinnerItem("Slider")); dropdown.setOptions(options, 0);
iconSpinnerItems.add(new IconSpinnerItem("Text")); dropdown.setTitle("Type");
iconSpinnerItems.add(new IconSpinnerItem("Dropdown"));
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);
dropdown.setGravity(Gravity.CENTER);
dropdown.setSpinnerAdapter(iconSpinnerAdapter);
dropdown.setItems(iconSpinnerItems);
dropdown.selectItemByIndex(0);
dropdown.setPadding(10,20,10,20);
dropdown.setBackgroundColor(0xf0000000);
dropdown.setTextColor(0xff00ff00);
dropdown.setTextSize(14.5f);
dropdown.setArrowGravity(SpinnerGravity.END);
dropdown.setArrowPadding(8);
// dropdown.setSpinnerItemHeight(46);
dropdown.setSpinnerPopupElevation(14);
builder.setView(dropdown); builder.setView(dropdown);
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { builder.setNegativeButton("Cancel", (dialog, which) -> dialog.cancel());
@Override builder.setPositiveButton("OK", (dialog, which) -> addField_Part_3(title, dropdown.getIndex()));
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
addField_Part_3(title, dropdown.getSelectedIndex());
}
});
builder.show(); builder.show();
} }
@@ -503,7 +520,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,89 +0,0 @@
package com.ridgebotics.ridgescout.ui.data;
import static com.ridgebotics.ridgescout.utility.DataManager.event;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TableRow;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.databinding.FragmentDataReportBinding;
import com.ridgebotics.ridgescout.types.frcMatch;
import com.ridgebotics.ridgescout.utility.DataManager;
import com.ridgebotics.ridgescout.utility.ollama.OllamaClient;
import com.ridgebotics.ridgescout.utility.ollama.PromptCreator;
import com.ridgebotics.ridgescout.utility.settingsManager;
public class ReportFragment extends Fragment {
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,
@Nullable Bundle savedInstanceState) {
binding = FragmentDataReportBinding.inflate(inflater, container, false);
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();
}
private void getPrompt(){
binding.AyEyeBox.setVisibility(View.VISIBLE);
String prompt = PromptCreator.genMatchPrompt(0);
binding.AyEyeBox.setText(prompt);
}
private void AIDataOverview(){
String prompt = binding.AyEyeBox.getText().toString();
binding.AyEyeBox.setText("");
OllamaClient.run(prompt, new OllamaClient.ollamaListener() {
@Override
public void onResponse(String response) {
// System.out.println(response);
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
binding.AyEyeBox.setText(binding.AyEyeBox.getText()+response);
}
});
}
@Override
public void onComplete() {
System.out.println(binding.AyEyeBox.getText());
}
});
}
}
@@ -1,82 +0,0 @@
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,6 +22,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.ui.CustomSpinnerView;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.settingsManager; 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;
@@ -30,11 +32,6 @@ import com.ridgebotics.ridgescout.types.frcTeam;
import com.ridgebotics.ridgescout.utility.DataManager; import com.ridgebotics.ridgescout.utility.DataManager;
import com.ridgebotics.ridgescout.utility.fileEditor; import com.ridgebotics.ridgescout.utility.fileEditor;
import com.google.android.material.divider.MaterialDivider; import com.google.android.material.divider.MaterialDivider;
import com.skydoves.powerspinner.IconSpinnerAdapter;
import com.skydoves.powerspinner.IconSpinnerItem;
import com.skydoves.powerspinner.OnSpinnerItemSelectedListener;
import com.skydoves.powerspinner.PowerSpinnerView;
import com.skydoves.powerspinner.SpinnerGravity;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -47,22 +44,30 @@ public class TeamsFragment extends Fragment {
team = tmpteam; team = tmpteam;
} }
private static final int background_color = 0x5000ff00;
private static final int unsaved_background_color = 0x2000ff00;
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) { @Nullable Bundle savedInstanceState) {
binding = FragmentDataTeamsBinding.inflate(inflater, container, false); binding = FragmentDataTeamsBinding.inflate(inflater, container, false);
binding.teamsArea.removeAllViews();
DataManager.reload_match_fields(); DataManager.reload_match_fields();
DataManager.reload_pit_fields(); DataManager.reload_pit_fields();
TableLayout table = new TableLayout(getContext()); binding.dataTypeSpinner.setTitle("Data Mode");
table.setStretchAllColumns(true);
binding.teamsArea.addView(table); List<String> options = new ArrayList<>();
options.add("Individual");
options.add("Compiled");
options.add("History");
binding.dataTypeSpinner.setOptions(options, 0);
binding.dataTypeSpinner.setOnClickListener((item, index) -> {
settingsManager.setDataMode(index);
loadTeam(index);
});
// binding.teamsMainElem.
loadTeam(settingsManager.getDataMode()); loadTeam(settingsManager.getDataMode());
@@ -70,117 +75,57 @@ public class TeamsFragment extends Fragment {
} }
public void loadTeam(int mode) { public void loadTeam(int mode) {
binding.teamsArea.removeAllViews();
LinearLayout ll = new LinearLayout(getContext()); // LinearLayout ll = new LinearLayout(getContext());
ll.setLayoutParams(new LinearLayout.LayoutParams( // ll.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, // ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT // ViewGroup.LayoutParams.WRAP_CONTENT
)); // ));
ll.setOrientation(LinearLayout.VERTICAL); // ll.setOrientation(LinearLayout.VERTICAL);
binding.teamsArea.addView(ll); // binding.teamsArea.addView(ll);
PowerSpinnerView dropdown = new PowerSpinnerView(getContext());
List<IconSpinnerItem> iconSpinnerItems = new ArrayList<>();
iconSpinnerItems.add(new IconSpinnerItem("Individual"));
iconSpinnerItems.add(new IconSpinnerItem("Compiled"));
iconSpinnerItems.add(new IconSpinnerItem("History"));
IconSpinnerAdapter iconSpinnerAdapter = new IconSpinnerAdapter(dropdown);
dropdown.setSpinnerAdapter(iconSpinnerAdapter);
dropdown.setItems(iconSpinnerItems);
dropdown.selectItemByIndex(0);
dropdown.setPadding(10,20,10,20);
dropdown.setBackgroundColor(0xf0000000);
dropdown.setTextColor(0xff00ff00);
dropdown.setTextSize(15);
dropdown.setArrowGravity(SpinnerGravity.END);
dropdown.setArrowPadding(8);
// dropdown.setSpinnerItemHeight(46);
dropdown.setSpinnerPopupElevation(14);
dropdown.selectItemByIndex(mode);
dropdown.setOnSpinnerItemSelectedListener(new OnSpinnerItemSelectedListener<IconSpinnerItem>() {
@Override
public void onItemSelected(int oldIndex, @Nullable IconSpinnerItem oldItem, int newIndex,
IconSpinnerItem newItem) {
settingsManager.setDataMode(newIndex);
loadTeam(newIndex);
}
});
ll.addView(dropdown);
binding.teamName2.setText(String.valueOf(team.teamNumber));
binding.teamDescription2.setText(team.getDescription());
// tv = new TextView(getContext());
// tv.setLayoutParams(new FrameLayout.LayoutParams(
// ViewGroup.LayoutParams.MATCH_PARENT,
// ViewGroup.LayoutParams.WRAP_CONTENT
// ));
// tv.setGravity(Gravity.CENTER_HORIZONTAL);
// tv.setText(team.getDescription());
// tv.setTextSize(16);
// ll.addView(tv);
TextView tv = new TextView(getContext()); add_pit_data(team);
tv.setLayoutParams(new FrameLayout.LayoutParams( add_match_data(team, mode);
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText(String.valueOf(team.teamNumber));
tv.setTextSize(28);
ll.addView(tv);
tv = new TextView(getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText(team.teamName);
tv.setTextSize(28);
ll.addView(tv);
tv = new TextView(getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText(team.getDescription());
tv.setTextSize(16);
ll.addView(tv);
add_pit_data(ll, team);
add_match_data(ll, team, mode);
} }
public void add_pit_data(LinearLayout ll, frcTeam team){ public void add_pit_data(frcTeam team){
binding.pitArea.removeAllViews();
final String filename = evcode+"-"+team.teamNumber+".pitscoutdata"; final String filename = evcode+"-"+team.teamNumber+".pitscoutdata";
ll.addView(new MaterialDivider(getContext())); // ll.addView(new MaterialDivider(getContext()));
TextView tv = new TextView(getContext()); // TextView tv = new TextView(getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams( // tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, // ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT // ViewGroup.LayoutParams.WRAP_CONTENT
)); // ));
tv.setGravity(Gravity.CENTER_HORIZONTAL); // tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setPadding(0,10,0,10); // tv.setPadding(0,10,0,10);
tv.setText("----- Pit data -----"); // tv.setText("----- Pit data -----");
tv.setTextSize(30); // tv.setTextSize(30);
ll.addView(tv); // ll.addView(tv);
ll.addView(new MaterialDivider(getContext())); // ll.addView(new MaterialDivider(getContext()));
if(!fileEditor.fileExist(filename)){ if(!fileEditor.fileExist(filename)){
tv = new TextView(getContext()); TextView tv = new TextView(getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams( tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT ViewGroup.LayoutParams.WRAP_CONTENT
@@ -188,13 +133,13 @@ public class TeamsFragment extends Fragment {
tv.setGravity(Gravity.CENTER_HORIZONTAL); tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText("No pit data has been collected!"); tv.setText("No pit data has been collected!");
tv.setTextSize(23); tv.setTextSize(23);
ll.addView(tv); binding.pitArea.addView(tv);
return; return;
} }
ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(filename, pit_values, pit_transferValues); ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(filename, pit_values, pit_transferValues);
tv = new TextView(getContext()); TextView tv = new TextView(getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams( tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT ViewGroup.LayoutParams.WRAP_CONTENT
@@ -203,7 +148,7 @@ public class TeamsFragment extends Fragment {
tv.setGravity(Gravity.CENTER_HORIZONTAL); tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText("Pit scouting by " + psda.username); tv.setText("Pit scouting by " + psda.username);
tv.setTextSize(30); tv.setTextSize(30);
ll.addView(tv); binding.pitArea.addView(tv);
for (int a = 0; a < psda.data.array.length; a++) { for (int a = 0; a < psda.data.array.length; a++) {
tv = new TextView(getContext()); tv = new TextView(getContext());
@@ -221,38 +166,24 @@ public class TeamsFragment extends Fragment {
} }
ll.addView(tv);
binding.pitArea.addView(tv);
pit_latest_values[a].add_individual_view(ll, psda.data.array[a]); pit_latest_values[a].add_individual_view(binding.pitArea, psda.data.array[a]);
} }
} }
private int matchIndex = 0;
public void add_match_data(frcTeam team, int mode){
public void add_match_data(LinearLayout ll, frcTeam team, int mode){ binding.matchArea.removeAllViews();
binding.individualViewSelector.setVisibility(View.GONE);
String[] files = fileEditor.getMatchesByTeamNum(evcode, team.teamNumber); String[] files = fileEditor.getMatchesByTeamNum(evcode, team.teamNumber);
ll.addView(new MaterialDivider(getContext()));
TextView tv = new TextView(getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText("----- Match data -----");
tv.setPadding(0,10,0,10);
tv.setTextSize(30);
ll.addView(tv);
ll.addView(new MaterialDivider(getContext()));
if(files.length == 0){ if(files.length == 0){
tv = new TextView(getContext()); TextView tv = new TextView(getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams( tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT ViewGroup.LayoutParams.WRAP_CONTENT
@@ -260,19 +191,19 @@ public class TeamsFragment extends Fragment {
tv.setGravity(Gravity.CENTER_HORIZONTAL); tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText("No match data has been collected!"); tv.setText("No match data has been collected!");
tv.setTextSize(23); tv.setTextSize(23);
ll.addView(tv); binding.matchArea.addView(tv);
return; return;
} }
switch (mode){ switch (mode){
case 0: case 0:
add_individual_views(ll,files); add_individual_views(files);
break; break;
case 1: case 1:
add_compiled_views(ll,files); add_compiled_views(files);
break; break;
case 2: case 2:
add_history_views(ll,files); add_history_views(files);
break; break;
} }
} }
@@ -280,12 +211,38 @@ public class TeamsFragment extends Fragment {
public void add_individual_views(LinearLayout ll, String[] files) { public void add_individual_views(String[] files) {
for (int i = 0; i < files.length; i++) {
String[] split = files[i].split("-");
int match_num = Integer.parseInt(split[1]);
ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(files[i], match_values, match_transferValues);
matchIndex = 0;
binding.individualViewSelector.setVisibility(View.VISIBLE);
binding.matchesPlusBtn.setOnClickListener(view -> {
matchIndex++;
update_individual_view(files);
});
binding.matchesMinusBtn.setOnClickListener(view -> {
matchIndex--;
update_individual_view(files);
});
update_individual_view(files);
}
private void update_individual_view(String[] files){
binding.matchesPlusBtn.setEnabled(matchIndex < files.length - 1);
binding.matchesMinusBtn.setEnabled(matchIndex > 0);
binding.matchArea.removeAllViews();
try {
String[] split = files[matchIndex].split("-");
int match_num = Integer.parseInt(split[1]);
binding.matchNum.setText(split[1]);
ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(files[matchIndex], match_values, match_transferValues);
TextView tv = new TextView(getContext()); TextView tv = new TextView(getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams( tv.setLayoutParams(new FrameLayout.LayoutParams(
@@ -296,29 +253,33 @@ public class TeamsFragment extends Fragment {
tv.setGravity(Gravity.CENTER_HORIZONTAL); tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText("M" + (match_num) + " " + split[2] + "-" + split[3] + " by " + psda.username); tv.setText("M" + (match_num) + " " + split[2] + "-" + split[3] + " by " + psda.username);
tv.setTextSize(30); tv.setTextSize(30);
ll.addView(tv); binding.matchArea.addView(tv);
for (int a = 0; a < psda.data.array.length; a++) { for (int i = 0; i < psda.data.array.length; i++) {
tv = new TextView(getContext()); tv = new TextView(getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams( tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT ViewGroup.LayoutParams.WRAP_CONTENT
)); ));
tv.setGravity(Gravity.CENTER_HORIZONTAL); tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText(psda.data.array[a].getName()); tv.setText(psda.data.array[i].getName());
tv.setTextSize(25); tv.setTextSize(25);
if (psda.data.array[a].isNull()) { if (psda.data.array[i].isNull()) {
tv.setBackgroundColor(0xffff0000); tv.setBackgroundColor(0xffff0000);
tv.setTextColor(0xff000000); tv.setTextColor(0xff000000);
} }
ll.addView(tv); binding.matchArea.addView(tv);
match_latest_values[a].add_individual_view(ll, psda.data.array[a]); match_latest_values[i].add_individual_view(binding.matchArea, psda.data.array[i]);
} }
}catch (Exception e){
e.printStackTrace();
AlertManager.alert("Warning!", "Failure to load file " + files[matchIndex]);
} }
} }
@@ -326,14 +287,18 @@ public class TeamsFragment extends Fragment {
public void add_compiled_views(LinearLayout ll, String[] files){ public void add_compiled_views(String[] files){
dataType[][] data = new dataType[match_latest_values.length][files.length]; dataType[][] data = new dataType[match_latest_values.length][files.length];
for (int i = 0; i < files.length; i++) { for (int i = 0; i < files.length; i++) {
try {
ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(files[i], match_values, match_transferValues); ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(files[i], match_values, match_transferValues);
for (int a = 0; a < data.length; a++) { for (int a = 0; a < data.length; a++) {
data[a][i] = psda.data.array[a]; data[a][i] = psda.data.array[a];
} }
} catch (Exception e){
e.printStackTrace();
AlertManager.alert("Warning!", "Failure to load file " + files[i]);
}
} }
for(int i = 0; i < match_latest_values.length; i++){ for(int i = 0; i < match_latest_values.length; i++){
@@ -346,9 +311,9 @@ public class TeamsFragment extends Fragment {
tv.setGravity(Gravity.CENTER_HORIZONTAL); tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText(match_latest_values[i].name); tv.setText(match_latest_values[i].name);
tv.setTextSize(30); tv.setTextSize(30);
ll.addView(tv); binding.matchArea.addView(tv);
match_latest_values[i].add_compiled_view(ll, data[i]); match_latest_values[i].add_compiled_view(binding.matchArea, data[i]);
} }
} }
@@ -356,14 +321,18 @@ public class TeamsFragment extends Fragment {
public void add_history_views(LinearLayout ll, String[] files){ public void add_history_views(String[] files){
dataType[][] data = new dataType[match_latest_values.length][files.length]; dataType[][] data = new dataType[match_latest_values.length][files.length];
for (int i = 0; i < files.length; i++) { for (int i = 0; i < files.length; i++) {
try {
ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(files[i], match_values, match_transferValues); ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(files[i], match_values, match_transferValues);
for (int a = 0; a < data.length; a++) { for (int a = 0; a < data.length; a++) {
data[a][i] = psda.data.array[a]; data[a][i] = psda.data.array[a];
} }
}catch (Exception e){
e.printStackTrace();
AlertManager.alert("Warning!", "Failure to load file " + files[i]);
}
} }
for(int i = 0; i < match_latest_values.length; i++){ for(int i = 0; i < match_latest_values.length; i++){
@@ -376,9 +345,9 @@ public class TeamsFragment extends Fragment {
tv.setGravity(Gravity.CENTER_HORIZONTAL); tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText(match_latest_values[i].name); tv.setText(match_latest_values[i].name);
tv.setTextSize(30); tv.setTextSize(30);
ll.addView(tv); binding.matchArea.addView(tv);
match_latest_values[i].add_history_view(ll, data[i]); match_latest_values[i].add_history_view(binding.matchArea, data[i]);
} }
} }
} }
@@ -0,0 +1,383 @@
package com.ridgebotics.ridgescout.ui.scouting;
import static android.widget.LinearLayout.HORIZONTAL;
import static com.ridgebotics.ridgescout.utility.DataManager.evcode;
import static com.ridgebotics.ridgescout.utility.DataManager.event;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.text.InputType;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TableRow;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.databinding.FragmentScoutingEventBinding;
import com.ridgebotics.ridgescout.types.frcTeam;
import com.ridgebotics.ridgescout.ui.CustomSpinnerView;
import com.ridgebotics.ridgescout.utility.DataManager;
import com.ridgebotics.ridgescout.utility.fileEditor;
import com.ridgebotics.ridgescout.types.frcEvent;
import com.ridgebotics.ridgescout.types.frcMatch;
import com.ridgebotics.ridgescout.utility.settingsManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class EventFragment extends Fragment {
FragmentScoutingEventBinding binding;
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentScoutingEventBinding.inflate(inflater, container, false);
reloadTable();
return binding.getRoot();
}
public void reloadTable() {
DataManager.reload_event();
binding.teamsTable.removeAllViews();
binding.teamsTable.setStretchAllColumns(true);
binding.matchTable.removeAllViews();
binding.matchTable.setStretchAllColumns(true);
add_pit_scouting(event);
add_match_scouting(event);
}
public static int color_found = 0x7f00ff00;
public static int color_not_found = 0x7f7f0000;
private void addTableText(TableRow tr, String textStr){
TextView text = new TextView(getContext());
text.setTextSize(18);
text.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); // Text align center
text.setText(textStr);
tr.addView(text);
}
public void add_pit_scouting(frcEvent event){
if(settingsManager.getCustomEvents()){
binding.teamsMinusBtn.setVisibility(View.VISIBLE);
binding.teamsMinusBtn.setOnClickListener(view -> removeTeam());
binding.teamsPlusBtn.setVisibility(View.VISIBLE);
binding.teamsPlusBtn.setOnClickListener(view -> addTeam());
}
int[] teams = new int[event.teams.size()];
for(int i = 0 ; i < event.teams.size(); i++){
teams[i] = event.teams.get(i).teamNumber;
}
Arrays.sort(teams);
TableRow tr = null;
for(int i=0; i < event.teams.size(); i++){
// frcTeam team = event.teams.get(i);
int num = teams[i];
if(i % 7 == 0){
if(i != 0)
binding.teamsTable.addView(tr);
tr = new TableRow(getContext());
}
TextView text = new TextView(getContext());
text.setTextSize(18);
text.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
text.setText(String.valueOf(num));
if(fileEditor.fileExist(event.eventCode + "-" + num + ".pitscoutdata")){
text.setBackgroundColor(color_found);
}else{
text.setBackgroundColor(color_not_found);
}
tr.addView(text);
}
if(tr != null)
binding.teamsTable.addView(tr);
}
public void add_match_scouting(frcEvent event){
if(settingsManager.getCustomEvents()){
binding.matchesMinusBtn.setVisibility(View.VISIBLE);
binding.matchesMinusBtn.setOnClickListener(view -> removeMatch());
binding.matchesPlusBtn.setVisibility(View.VISIBLE);
binding.matchesPlusBtn.setOnClickListener(view -> addMatch());
}
TableRow tr = new TableRow(getContext());
addTableText(tr, "#");
addTableText(tr, "Red-1");
addTableText(tr, "Red-2");
addTableText(tr, "Red-3");
addTableText(tr, "Blue-1");
addTableText(tr, "Blue-2");
addTableText(tr, "Blue-3");
binding.matchTable.addView(tr);
for(frcMatch match : event.matches){
tr = new TableRow(getContext());
addTableText(tr, String.valueOf(match.matchIndex));
//
for(int i=0;i<6;i++){
TextView text = new TextView(getContext());
text.setTextSize(18);
text.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
int team_num;
String alliance_position;
if(i < 3){
team_num = match.redAlliance[i];
alliance_position = "red-"+(i+1);
}else{
team_num = match.blueAlliance[i-3];
alliance_position = "blue-"+(i-2);
}
text.setText(String.valueOf(team_num));
if(fileEditor.fileExist(event.eventCode + "-" + match.matchIndex + "-" + alliance_position + "-" + team_num + ".matchscoutdata")){
text.setBackgroundColor(color_found);
}else{
text.setBackgroundColor(color_not_found);
}
tr.addView(text);
}
binding.matchTable.addView(tr);
}
}
public void addTeam(){
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle("Add team");
LinearLayout ll = new LinearLayout(getContext());
ll.setOrientation(LinearLayout.VERTICAL);
EditText teamNum = new EditText(getContext());
teamNum.setHint("Team Number");
teamNum.setInputType(InputType.TYPE_CLASS_NUMBER);
ll.addView(teamNum);
EditText teamName = new EditText(getContext());
teamName.setHint("Team Name");
ll.addView(teamName);
EditText school = new EditText(getContext());
school.setHint("School");
ll.addView(school);
EditText city = new EditText(getContext());
city.setHint("City");
ll.addView(city);
EditText stateOrProv = new EditText(getContext());
stateOrProv.setHint("State Or Province");
ll.addView(stateOrProv);
EditText country = new EditText(getContext());
country.setHint("Country");
ll.addView(country);
EditText startingYear = new EditText(getContext());
startingYear.setHint("Starting Year");
startingYear.setInputType(InputType.TYPE_CLASS_NUMBER);
ll.addView(startingYear);
builder.setView(ll);
builder.setNeutralButton("Cancel", (dialogInterface, i) -> {});
builder.setPositiveButton("OK", (dialogInterface, i) -> {
if(teamNum.getText().toString().isEmpty() || teamName.getText().toString().isEmpty()) return;
frcTeam team = new frcTeam();
team.teamNumber = Integer.parseInt(teamNum.getText().toString());
team.teamName = teamName.getText().toString();
team.school = school.getText().toString();
team.city = city.getText().toString();
team.country = country.getText().toString();
team.stateOrProv = stateOrProv.getText().toString();
team.startingYear = safeToInt(startingYear.getText().toString());
event.teams.add(team);
fileEditor.setEvent(event);
reloadTable();
});
builder.create().show();
}
public void removeTeam(){
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle("Remove team");
CustomSpinnerView dropdown = new CustomSpinnerView(getContext());
List<String> teamNums = new ArrayList<>();
for(int i = 0 ;i < event.teams.size(); i++)
teamNums.add(String.valueOf(event.teams.get(i).teamNumber));
dropdown.setTitle("Teams");
dropdown.setOptions(teamNums, -1);
builder.setView(dropdown);
builder.setNeutralButton("Cancel", (dialogInterface, i) -> {});
builder.setPositiveButton("OK", (dialogInterface, i) -> {
int index = dropdown.getIndex();
System.out.println(index);
if(!(index >= 0 && index < teamNums.size())) return;
event.teams.remove(index);
fileEditor.setEvent(event);
reloadTable();
});
builder.create().show();
}
public void addMatch(){
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle("Add match");
List<String> teamNums = new ArrayList<>();
for(int i = 0 ;i < event.teams.size(); i++)
teamNums.add(String.valueOf(event.teams.get(i).teamNumber));
ScrollView sv = new ScrollView(getContext());
// sv.setLayoutDirection(ScrollView.SCROLL_AXIS_VERTICAL);
LinearLayout ll = new LinearLayout(getContext());
ll.setOrientation(LinearLayout.VERTICAL);
sv.addView(ll);
CustomSpinnerView Red1dropdown = new CustomSpinnerView(getContext());
Red1dropdown.setTitle("Red-1");
Red1dropdown.setOptions(teamNums, -1);
ll.addView(Red1dropdown);
CustomSpinnerView Red2dropdown = new CustomSpinnerView(getContext());
Red2dropdown.setTitle("Red-2");
Red2dropdown.setOptions(teamNums, -1);
ll.addView(Red2dropdown);
CustomSpinnerView Red3dropdown = new CustomSpinnerView(getContext());
Red3dropdown.setTitle("Red-3");
Red3dropdown.setOptions(teamNums, -1);
ll.addView(Red3dropdown);
CustomSpinnerView Blue1dropdown = new CustomSpinnerView(getContext());
Blue1dropdown.setTitle("Blue-1");
Blue1dropdown.setOptions(teamNums, -1);
ll.addView(Blue1dropdown);
CustomSpinnerView Blue2dropdown = new CustomSpinnerView(getContext());
Blue2dropdown.setTitle("Blue-2");
Blue2dropdown.setOptions(teamNums, -1);
ll.addView(Blue2dropdown);
CustomSpinnerView Blue3dropdown = new CustomSpinnerView(getContext());
Blue3dropdown.setTitle("Blue-3");
Blue3dropdown.setOptions(teamNums, -1);
ll.addView(Blue3dropdown);
builder.setView(sv);
builder.setNeutralButton("Cancel", (dialogInterface, i) -> {});
builder.setPositiveButton("OK", (dialogInterface, i) -> {
int red1index = Red1dropdown.getIndex();
int red2index = Red2dropdown.getIndex();
int red3index = Red3dropdown.getIndex();
int blue1index = Blue1dropdown.getIndex();
int blue2index = Blue2dropdown.getIndex();
int blue3index = Blue3dropdown.getIndex();
if(red1index == -1 || red2index == -1 || red3index == -1 || blue1index == -1 || blue2index == -1 || blue3index == -1) return;
frcMatch match = new frcMatch();
match.matchIndex = event.matches.size() + 1;
match.redAlliance = new int[] {
event.teams.get(red1index).teamNumber,
event.teams.get(red2index).teamNumber,
event.teams.get(red3index).teamNumber
};
match.blueAlliance = new int[] {
event.teams.get(blue1index).teamNumber,
event.teams.get(blue2index).teamNumber,
event.teams.get(blue3index).teamNumber
};
event.matches.add(match);
fileEditor.setEvent(event);
reloadTable();
});
builder.create().show();
}
public void removeMatch(){
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle("Remove match");
List<String> matches = new ArrayList<>();
for(int i = 0 ;i < event.matches.size(); i++) {
frcMatch match = event.matches.get(i);
matches.add(match.matchIndex + " - " + Arrays.toString(match.redAlliance) + ", " + Arrays.toString(match.blueAlliance));
}
CustomSpinnerView dropdown = new CustomSpinnerView(getContext());
dropdown.setTitle("Matches");
dropdown.setOptions(matches, -1);
builder.setView(dropdown);
builder.setNeutralButton("Cancel", (dialogInterface, i) -> {});
builder.setPositiveButton("OK", (dialogInterface, i) -> {
if(dropdown.getIndex() == -1) return;
event.matches.remove(dropdown.getIndex());
fileEditor.setEvent(event);
reloadTable();
});
builder.create().show();
}
public int safeToInt(String str){
try{
return Integer.parseInt(str);
}catch (Exception e){
return 0;
}
}
}
@@ -72,7 +72,7 @@ public class FieldPosView extends FrameLayout {
return false; return false;
}); });
setImageResource(R.drawable.field_2024); setImageResource(R.drawable.field_2025);
} }
@@ -14,6 +14,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.google.android.material.divider.MaterialDivider;
import com.ridgebotics.ridgescout.ui.ToggleTitleView;
import com.ridgebotics.ridgescout.utility.settingsManager; 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;
@@ -63,10 +65,6 @@ public class MatchScoutingFragment extends Fragment {
cur_match_num = settingsManager.getMatchNum();
update_match_num();
binding.nextButton.setOnClickListener(v -> { binding.nextButton.setOnClickListener(v -> {
if(edited) save(); if(edited) save();
settingsManager.setMatchNum(cur_match_num+1); settingsManager.setMatchNum(cur_match_num+1);
@@ -103,6 +101,14 @@ public class MatchScoutingFragment extends Fragment {
// if(edited) save(); // if(edited) save();
// }); // });
cur_match_num = settingsManager.getMatchNum();
if(cur_match_num >= event.matches.size()) {
cur_match_num = 0;
settingsManager.setMatchNum(0);
}
update_match_num();
create_fields(); create_fields();
update_scouting_data(); update_scouting_data();
@@ -134,15 +140,10 @@ public class MatchScoutingFragment extends Fragment {
int cur_match_num; int cur_match_num;
String username; String username;
String filename; String filename;
boolean edited = false; boolean edited = false;
ToggleTitleView[] titles;
TextView[] titles;
AutoSaveManager asm = new AutoSaveManager(this::save); AutoSaveManager asm = new AutoSaveManager(this::save);
ArrayList<dataType> dataTypes;
public void save(){ public void save(){
@@ -186,48 +187,41 @@ public class MatchScoutingFragment extends Fragment {
asm.stop(); asm.stop();
} }
titles = new TextView[DataManager.match_latest_values.length]; titles = new ToggleTitleView[DataManager.match_latest_values.length];
for(int i = 0 ; i < DataManager.match_latest_values.length; i++) { for(int i = 0 ; i < DataManager.match_latest_values.length; i++) {
final TextView tv = new TextView(getContext()); binding.MatchScoutArea.addView(new MaterialDivider(getContext()));
tv.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
tv.setText(DataManager.match_latest_values[i].name);
tv.setPadding(8,8,8,8);
tv.setTextSize(24);
titles[i] = tv;
default_text_color = tv.getCurrentTextColor();
final View v = DataManager.match_latest_values[i].createView(getContext(), new Function<dataType, Integer>() { final ToggleTitleView ttv = new ToggleTitleView(getContext());
@Override ttv.setTitle(DataManager.match_latest_values[i].name);
public Integer apply(dataType dataType) { ttv.setDescription(DataManager.match_latest_values[i].description);
titles[i] = ttv;
final View v = DataManager.match_latest_values[i].createView(getContext(), dataType -> {
// edited = true; // edited = true;
if(asm.isRunning) if(asm.isRunning)
update_asm(); update_asm();
return 0; return 0;
}
}); });
binding.MatchScoutArea.addView(tv); binding.MatchScoutArea.addView(ttv);
int fi = i; int fi = i;
tv.setOnClickListener(p -> {
// boolean blank = !latest_values[fi].getViewValue().isNull();
// System.out.println(blank); ttv.setOnToggleListener(enabled -> {
if(asm.isRunning) if(asm.isRunning)
update_asm(); update_asm();
if(!DataManager.match_latest_values[fi].isBlank){ // System.out.println("Checked!");
tv.setBackgroundColor(0xffff0000);
tv.setTextColor(0xff000000); if(enabled){
DataManager.match_latest_values[fi].nullify(); DataManager.match_latest_values[fi].nullify();
}else{ }else
tv.setBackgroundColor(0x00000000);
tv.setTextColor(default_text_color);
DataManager.match_latest_values[fi].setViewValue(DataManager.match_latest_values[fi].default_value); DataManager.match_latest_values[fi].setViewValue(DataManager.match_latest_values[fi].default_value);
}
}); });
binding.MatchScoutArea.addView(v); binding.MatchScoutArea.addView(v);
} }
} }
@@ -297,6 +291,13 @@ public class MatchScoutingFragment extends Fragment {
frcMatch match = event.matches.get(cur_match_num); frcMatch match = event.matches.get(cur_match_num);
frcTeam team = get_team(match); frcTeam team = get_team(match);
if(team == null) {
AlertManager.error("This team does not exist!");
binding.teamName.setText("ERROR!");
binding.teamDescription.setText("ERROR!");
return;
}
binding.teamName.setText(team.teamName); binding.teamName.setText(team.teamName);
binding.teamDescription.setText(team.getDescription()); binding.teamDescription.setText(team.getDescription());
@@ -310,8 +311,14 @@ public class MatchScoutingFragment extends Fragment {
default_fields(); default_fields();
set_indicator_color(unsaved_color); set_indicator_color(unsaved_color);
}else{ }else{
try {
get_fields(); get_fields();
set_indicator_color(saved_color); set_indicator_color(saved_color);
} catch (Exception e){
AlertManager.error(e);
default_fields();
set_indicator_color(unsaved_color);
}
} }
asm.start(); asm.start();
@@ -325,8 +332,7 @@ public class MatchScoutingFragment extends Fragment {
inputType input = DataManager.match_latest_values[i]; inputType input = DataManager.match_latest_values[i];
input.setViewValue(input.default_value); input.setViewValue(input.default_value);
titles[i].setBackgroundColor(0x00000000); titles[i].enable();
titles[i].setTextColor(default_text_color);
} }
} }
@@ -337,6 +343,7 @@ public class MatchScoutingFragment extends Fragment {
ScoutingDataWriter.ParsedScoutingDataResult psdr = ScoutingDataWriter.load(filename, DataManager.match_values, DataManager.match_transferValues); ScoutingDataWriter.ParsedScoutingDataResult psdr = ScoutingDataWriter.load(filename, DataManager.match_values, DataManager.match_transferValues);
dataType[] types = psdr.data.array; dataType[] types = psdr.data.array;
for(int i = 0; i < DataManager.match_latest_values.length; i++){ for(int i = 0; i < DataManager.match_latest_values.length; i++){
// types[i] = latest_values[i].getViewValue(); // types[i] = latest_values[i].getViewValue();
try { try {
@@ -346,14 +353,8 @@ public class MatchScoutingFragment extends Fragment {
DataManager.match_latest_values[i].setViewValue(DataManager.match_latest_values[i].default_value); DataManager.match_latest_values[i].setViewValue(DataManager.match_latest_values[i].default_value);
} }
titles[i].setEnabled(DataManager.match_latest_values[i].isBlank);
if(DataManager.match_latest_values[i].isBlank){
titles[i].setBackgroundColor(0xffff0000);
titles[i].setTextColor(0xff000000);
}else{
titles[i].setBackgroundColor(0x00000000);
titles[i].setTextColor(default_text_color);
}
} }
} }
@@ -52,7 +52,7 @@ public class MultiFieldPosView extends FrameLayout {
imageView.setAdjustViewBounds(true); imageView.setAdjustViewBounds(true);
addView(imageView); addView(imageView);
setImageResource(R.drawable.field_2024); setImageResource(R.drawable.field_2025);
} }
@@ -15,6 +15,9 @@ 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.ui.ToggleTitleView;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.settingsManager; 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;
@@ -58,7 +61,7 @@ public class PitScoutingFragment extends Fragment {
String filename; String filename;
String username; String username;
TextView[] titles; ToggleTitleView[] titles;
AutoSaveManager asm = new AutoSaveManager(this::save); AutoSaveManager asm = new AutoSaveManager(this::save);
@@ -74,9 +77,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");
} }
@@ -117,46 +121,45 @@ public class PitScoutingFragment extends Fragment {
default_fields(); default_fields();
set_indicator_color(unsaved_color); set_indicator_color(unsaved_color);
}else{ }else{
try {
get_fields(); get_fields();
set_indicator_color(saved_color); set_indicator_color(saved_color);
} catch (Exception e){
AlertManager.error(e);
default_fields();
set_indicator_color(unsaved_color);
}
} }
asm.start(); asm.start();
} }
private int default_text_color = 0;
private void create_fields() { private void create_fields() {
if(asm.isRunning){ if(asm.isRunning){
asm.stop(); asm.stop();
} }
titles = new TextView[pit_latest_values.length]; titles = new ToggleTitleView[pit_latest_values.length];
for(int i = 0 ; i < pit_latest_values.length; i++) { for(int i = 0 ; i < pit_latest_values.length; i++) {
TextView tv = new TextView(getContext()); binding.pitScoutArea.addView(new MaterialDivider(getContext()));
tv.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
tv.setText(pit_latest_values[i].name); ToggleTitleView ttv = new ToggleTitleView(getContext());
tv.setTextSize(24); ttv.setTitle(pit_latest_values[i].name);
tv.setPadding(8,8,8,8); ttv.setDescription(pit_latest_values[i].description);
titles[i] = tv; titles[i] = ttv;
binding.pitScoutArea.addView(tv); binding.pitScoutArea.addView(ttv);
default_text_color = tv.getCurrentTextColor();
int fi = i; int fi = i;
tv.setOnClickListener(p -> { ttv.setOnToggleListener(enabled -> {
update_asm(); update_asm();
if(!pit_latest_values[fi].isBlank){ if(enabled){
tv.setBackgroundColor(0xffff0000);
tv.setTextColor(0xff000000);
pit_latest_values[fi].nullify(); pit_latest_values[fi].nullify();
}else{ }else{
tv.setBackgroundColor(0x00000000);
tv.setTextColor(default_text_color);
pit_latest_values[fi].setViewValue(pit_latest_values[fi].default_value); pit_latest_values[fi].setViewValue(pit_latest_values[fi].default_value);
} }
}); });
@@ -179,9 +182,7 @@ public class PitScoutingFragment extends Fragment {
for(int i = 0; i < pit_latest_values.length; i++){ for(int i = 0; i < pit_latest_values.length; i++){
inputType input = pit_latest_values[i]; inputType input = pit_latest_values[i];
input.setViewValue(input.default_value); input.setViewValue(input.default_value);
titles[i].enable();
titles[i].setBackgroundColor(0x00000000);
titles[i].setTextColor(default_text_color);
} }
} }
@@ -191,15 +192,12 @@ public class PitScoutingFragment extends Fragment {
dataType[] types = psdr.data.array; dataType[] types = psdr.data.array;
for(int i = 0; i < pit_latest_values.length; i++){ for(int i = 0; i < pit_latest_values.length; i++){
// types[i] = latest_values[i].getViewValue();
pit_latest_values[i].setViewValue(types[i]); pit_latest_values[i].setViewValue(types[i]);
if(pit_latest_values[i].isBlank){ if(pit_latest_values[i].isBlank){
titles[i].setBackgroundColor(0xffff0000); titles[i].disable();
titles[i].setTextColor(0xff000000);
}else{ }else{
titles[i].setBackgroundColor(0x00000000); titles[i].enable();
titles[i].setTextColor(default_text_color);
} }
} }
} }
@@ -1,27 +1,35 @@
package com.ridgebotics.ridgescout.ui.scouting; package com.ridgebotics.ridgescout.ui.scouting;
import static android.widget.LinearLayout.VERTICAL;
import static androidx.navigation.fragment.FragmentKt.findNavController; import static androidx.navigation.fragment.FragmentKt.findNavController;
import static com.ridgebotics.ridgescout.utility.DataManager.event; import static com.ridgebotics.ridgescout.utility.DataManager.event;
import android.app.AlertDialog;
import android.os.Bundle; import android.os.Bundle;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.LinearLayout;
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.ridgebotics.ridgescout.R; import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.types.frcEvent;
import com.ridgebotics.ridgescout.utility.fileEditor;
import com.ridgebotics.ridgescout.utility.settingsManager; 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;
import com.ridgebotics.ridgescout.utility.DataManager; import com.ridgebotics.ridgescout.utility.DataManager;
import java.util.ArrayList;
public class ScoutingFragment extends Fragment { public class ScoutingFragment extends Fragment {
private FragmentScoutingBinding binding; private FragmentScoutingBinding binding;
@@ -34,21 +42,62 @@ public class ScoutingFragment extends Fragment {
binding.buttons.setVisibility(View.VISIBLE); binding.buttons.setVisibility(View.VISIBLE);
String evcode = settingsManager.getEVCode(); DataManager.reload_event();
if(evcode.equals("unset")){ if(settingsManager.getCustomEvents()){
binding.eventAddButton.setVisibility(View.VISIBLE);
binding.eventAddButton.setOnClickListener(view -> {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle("Chose event name");
LinearLayout layout = new LinearLayout(getContext());
layout.setOrientation(VERTICAL);
EditText eventName = new EditText(getContext());
eventName.setHint("Event Name");
EditText eventCode = new EditText(getContext());
eventCode.setHint("Event Code");
layout.addView(eventName);
layout.addView(eventCode);
builder.setPositiveButton("Create", (dialog, which) -> {
String name = eventName.getText().toString();
String code = eventCode.getText().toString();
if(name.isEmpty() || code.isEmpty()) return;
frcEvent event = new frcEvent();
event.name = name;
event.eventCode = code;
event.teams = new ArrayList<>();
event.matches = new ArrayList<>();
fileEditor.setEvent(event);
});
builder.setNeutralButton("Cancel", (dialog, which) -> {});
builder.setView(layout);
builder.create().show();
});
}
if(event == null){
binding.noEventError.setVisibility(View.VISIBLE); binding.noEventError.setVisibility(View.VISIBLE);
binding.matchScoutingButton.setEnabled(false); binding.matchScoutingButton.setEnabled(false);
binding.pitScoutingButton.setEnabled(false); binding.pitScoutingButton.setEnabled(false);
binding.statusButton.setEnabled(false); binding.eventButton.setEnabled(false);
is_main_page = false; is_main_page = false;
return binding.getRoot(); return binding.getRoot();
} }
DataManager.reload_event(); if(event.matches.isEmpty()){
binding.matchScoutingButton.setEnabled(false);
}
if(event.matches.isEmpty()) if(event.teams.isEmpty()){
binding.matchScoutingButton.setVisibility(View.GONE); binding.pitScoutingButton.setEnabled(false);
}
binding.matchScoutingButton.setOnClickListener(v -> { binding.matchScoutingButton.setOnClickListener(v -> {
findNavController(this).navigate(R.id.action_navigation_scouting_to_navigation_match_scouting); findNavController(this).navigate(R.id.action_navigation_scouting_to_navigation_match_scouting);
@@ -56,18 +105,15 @@ public class ScoutingFragment extends Fragment {
binding.pitScoutingButton.setOnClickListener(v -> { binding.pitScoutingButton.setOnClickListener(v -> {
TeamSelectorFragment.setPits_mode(true); TeamSelectorFragment.setPits_mode(true);
TeamSelectorFragment.setOnSelect(new TeamSelectorFragment.onTeamSelected() { TeamSelectorFragment.setOnSelect((self, team) -> {
@Override
public void onSelect(TeamSelectorFragment self, frcTeam team) {
PitScoutingFragment.setTeam(team); PitScoutingFragment.setTeam(team);
findNavController(self).navigate(R.id.action_navigation_team_selector_to_navigation_pit_scouting); findNavController(self).navigate(R.id.action_navigation_team_selector_to_navigation_pit_scouting);
}
}); });
findNavController(this).navigate(R.id.action_navigation_scouting_to_navigation_team_selector); findNavController(this).navigate(R.id.action_navigation_scouting_to_navigation_team_selector);
}); });
binding.statusButton.setOnClickListener(v -> { binding.eventButton.setOnClickListener(v -> {
findNavController(this).navigate(R.id.action_navigation_scouting_to_navigation_scouting_status); findNavController(this).navigate(R.id.action_navigation_scouting_to_navigation_scouting_event);
}); });
return binding.getRoot(); return binding.getRoot();
@@ -83,23 +129,17 @@ public class ScoutingFragment extends Fragment {
getView().setFocusableInTouchMode(true); getView().setFocusableInTouchMode(true);
getView().requestFocus(); getView().requestFocus();
getView().setOnKeyListener(new View.OnKeyListener() { getView().setOnKeyListener((v, keyCode, event) -> {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP if (event.getAction() == KeyEvent.ACTION_UP
&& keyCode == KeyEvent.KEYCODE_BACK && keyCode == KeyEvent.KEYCODE_BACK
&& !is_main_page){ && !is_main_page){
// binding.buttons.setVisibility(View.VISIBLE);
// binding.matchScoutingView.setVisibility(View.GONE);
// binding.pitScoutingView.setVisibility(View.GONE);
is_main_page = true; is_main_page = true;
return true; return true;
} }
return false; return false;
}
}); });
} }
@@ -1,166 +0,0 @@
package com.ridgebotics.ridgescout.ui.scouting;
import static com.ridgebotics.ridgescout.utility.DataManager.event;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TableRow;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.databinding.FragmentScoutingStatusBinding;
import com.ridgebotics.ridgescout.utility.DataManager;
import com.ridgebotics.ridgescout.utility.fileEditor;
import com.ridgebotics.ridgescout.types.frcEvent;
import com.ridgebotics.ridgescout.types.frcMatch;
import java.util.Arrays;
public class StatusFragment extends Fragment {
FragmentScoutingStatusBinding binding;
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentScoutingStatusBinding.inflate(inflater, container, false);
DataManager.reload_event();
binding.matchTable.removeAllViews();
binding.matchTable.setStretchAllColumns(true);
add_pit_scouting(event);
add_match_scouting(event);
return binding.getRoot();
}
public static int color_found = 0x7f00ff00;
public static int color_not_found = 0x7f7f0000;
private void addTableText(TableRow tr, String textStr){
TextView text = new TextView(getContext());
text.setTextSize(18);
text.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); // Text align center
text.setText(textStr);
tr.addView(text);
}
public void add_pit_scouting(frcEvent event){
TextView tv = new TextView(getContext());
tv.setLayoutParams(new TableRow.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText("Pit Scouting");
tv.setTextSize(28);
binding.matchTable.addView(tv);
int[] teams = new int[event.teams.size()];
for(int i = 0 ; i < event.teams.size(); i++){
teams[i] = event.teams.get(i).teamNumber;
}
Arrays.sort(teams);
TableRow tr = null;
for(int i=0; i < event.teams.size(); i++){
// frcTeam team = event.teams.get(i);
int num = teams[i];
if(i % 7 == 0){
if(i != 0)
binding.matchTable.addView(tr);
tr = new TableRow(getContext());
}
TextView text = new TextView(getContext());
text.setTextSize(18);
text.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
text.setText(String.valueOf(num));
if(fileEditor.fileExist(event.eventCode + "-" + num + ".pitscoutdata")){
text.setBackgroundColor(color_found);
}else{
text.setBackgroundColor(color_not_found);
}
tr.addView(text);
}
if(tr != null)
binding.matchTable.addView(tr);
}
public void add_match_scouting(frcEvent event){
TextView tv = new TextView(getContext());
tv.setLayoutParams(new TableRow.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText("Match Scouting");
tv.setTextSize(28);
binding.matchTable.addView(tv);
TableRow tr = new TableRow(getContext());
addTableText(tr, "#");
addTableText(tr, "Red-1");
addTableText(tr, "Red-2");
addTableText(tr, "Red-3");
addTableText(tr, "Blue-1");
addTableText(tr, "Blue-2");
addTableText(tr, "Blue-3");
binding.matchTable.addView(tr);
for(frcMatch match : event.matches){
tr = new TableRow(getContext());
addTableText(tr, String.valueOf(match.matchIndex));
//
for(int i=0;i<6;i++){
TextView text = new TextView(getContext());
text.setTextSize(18);
text.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
int team_num;
String alliance_position;
if(i < 3){
team_num = match.redAlliance[i];
alliance_position = "red-"+(i+1);
}else{
team_num = match.blueAlliance[i-3];
alliance_position = "blue-"+(i-2);
}
text.setText(String.valueOf(team_num));
if(fileEditor.fileExist(event.eventCode + "-" + match.matchIndex + "-" + alliance_position + "-" + team_num + ".matchscoutdata")){
text.setBackgroundColor(color_found);
}else{
text.setBackgroundColor(color_not_found);
}
tr.addView(text);
}
// addTableText(tr, String.valueOf(match.matchIndex));
// addTableText(tr, String.valueOf(match.blueAlliance[0]));
// addTableText(tr, String.valueOf(match.blueAlliance[1]));
// addTableText(tr, String.valueOf(match.blueAlliance[2]));
// addTableText(tr, String.valueOf(match.redAlliance[0]));
// addTableText(tr, String.valueOf(match.redAlliance[1]));
// addTableText(tr, String.valueOf(match.redAlliance[2]));
// if (toggle) {
// tr.setBackgroundColor(0x30000000);
// }
//
// toggle = !toggle;
binding.matchTable.addView(tr);
}
}
}
@@ -1,57 +1,48 @@
package com.ridgebotics.ridgescout.ui.settings; package com.ridgebotics.ridgescout.ui.settings;
import android.app.AlertDialog; import static com.ridgebotics.ridgescout.utility.settingsManager.AllyPosKey;
import static com.ridgebotics.ridgescout.utility.settingsManager.CustomEventsKey;
import static com.ridgebotics.ridgescout.utility.settingsManager.SelEVCodeKey;
import static com.ridgebotics.ridgescout.utility.settingsManager.UnameKey;
import static com.ridgebotics.ridgescout.utility.settingsManager.WifiModeKey;
import static com.ridgebotics.ridgescout.utility.settingsManager.YearNumKey;
import static com.ridgebotics.ridgescout.utility.settingsManager.defaults;
import static com.ridgebotics.ridgescout.utility.settingsManager.getEditor;
import static com.ridgebotics.ridgescout.utility.settingsManager.prefs;
import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable; import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ArrayAdapter; import android.widget.LinearLayout;
import android.widget.Button; import android.widget.TableLayout;
import android.widget.CheckBox; import android.widget.TextView;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.Spinner;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import com.google.android.material.card.MaterialCardView;
import com.google.android.material.checkbox.MaterialCheckBox;
import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;
import com.ridgebotics.ridgescout.databinding.FragmentSettingsBinding; import com.ridgebotics.ridgescout.databinding.FragmentSettingsBinding;
import com.ridgebotics.ridgescout.types.data.intType; import com.ridgebotics.ridgescout.ui.CustomSpinnerPopup;
import com.ridgebotics.ridgescout.ui.CustomSpinnerView;
import com.ridgebotics.ridgescout.utility.fileEditor; import com.ridgebotics.ridgescout.utility.fileEditor;
import com.ridgebotics.ridgescout.utility.settingsManager; import com.ridgebotics.ridgescout.utility.settingsManager;
import com.skydoves.powerspinner.IconSpinnerAdapter;
import com.skydoves.powerspinner.IconSpinnerItem;
import com.skydoves.powerspinner.OnSpinnerItemSelectedListener;
import com.skydoves.powerspinner.PowerSpinnerView;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List; import java.util.List;
public class settingsFragment extends Fragment { public class settingsFragment extends Fragment {
private FragmentSettingsBinding binding; private FragmentSettingsBinding binding;
private android.widget.ScrollView ScrollArea;
private android.widget.TableLayout Table;
private void setDropdownItems(Spinner dropdown, String[] items){
ArrayAdapter<String> adapter = new ArrayAdapter<>(requireActivity(), android.R.layout.simple_spinner_item, items);
dropdown.setAdapter(adapter);
}
private int safeToInt(String num){
if(num.isEmpty())
return 0;
try {
return Integer.parseInt(num);
}catch (NumberFormatException e){
return 0;
}
}
public View onCreateView(@NonNull LayoutInflater inflater, public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) { ViewGroup container, Bundle savedInstanceState) {
@@ -59,173 +50,32 @@ 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(settingsManager.getUsername());
username.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
settingsManager.setUsername(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 = settingsManager.getEVCode();
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) {
settingsManager.setEVCode(newItem.getText().toString());
}
});
PowerSpinnerView alliance_pos_spinnerView = binding.alliancePosDropdown;
List<IconSpinnerItem> alliance_pos_iconSpinnerItems = new ArrayList<>();
String target_alliance_pos = settingsManager.getAllyPos();
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++){ SettingsManager manager = new SettingsManager(getContext());
if(alliance_pos_list[i].equals(target_alliance_pos)){
alliance_pos_target_index = i;
}
alliance_pos_iconSpinnerItems.add(new IconSpinnerItem(alliance_pos_list[i]));
}
IconSpinnerAdapter alliance_pos_iconSpinnerAdapter = new IconSpinnerAdapter(alliance_pos_spinnerView);
alliance_pos_spinnerView.setSpinnerAdapter(alliance_pos_iconSpinnerAdapter);
alliance_pos_spinnerView.setItems(alliance_pos_iconSpinnerItems);
alliance_pos_spinnerView.setLifecycleOwner(this);
if(alliance_pos_target_index != -1){
alliance_pos_spinnerView.selectItemByIndex(alliance_pos_target_index);
}
alliance_pos_spinnerView.setOnSpinnerItemSelectedListener(new OnSpinnerItemSelectedListener<IconSpinnerItem>() {
@Override
public void onItemSelected(int oldIndex, @Nullable IconSpinnerItem oldItem, int newIndex,
IconSpinnerItem newItem) {
settingsManager.setAllyPos(newItem.getText().toString());
}
});
manager.addItem(new CheckboxSettingsItem(CustomEventsKey, "Custom Events"));
StringSettingsItem FTPServer = new StringSettingsItem(settingsManager.FTPServer, "FTP Server (Sync)");
manager.addItem(FTPServer);
CheckboxSettingsItem FTPSendMetaFiles = new CheckboxSettingsItem(settingsManager.FTPSendMetaFiles, "Sync meta files");
manager.addItem(FTPSendMetaFiles);
CheckboxSettingsItem FTPEnabled = new CheckboxSettingsItem(settingsManager.FTPEnabled, "FTP Enabled", FTPServer, FTPSendMetaFiles);
manager.addItem(FTPEnabled);
manager.addItem(new CheckboxSettingsItem(WifiModeKey, "Wifi Mode", FTPEnabled));
manager.addItem(new NumberSettingsItem(YearNumKey, "Year", 0, 9999));
manager.addItem(new DropdownSettingsItem(AllyPosKey, "Alliance Pos", alliance_pos_list));
manager.addItem(new DropdownSettingsItem(SelEVCodeKey, "Event Code", fileEditor.getEventList().toArray(new String[0])));
manager.addItem(new StringSettingsItem(UnameKey, "Username"));
manager.getView(binding.SettingsTable);
//
// 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(settingsManager.getTeamNum()));
team_num.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
settingsManager.setTeamNum(safeToInt(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) {
settingsManager.setWifiMode(isChecked);
}
});
wifi_mode.setChecked(settingsManager.getWifiMode());
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) -> {
// settingsManager.settings.defaultSettings();
username.setText(settingsManager.getUsername());
spinnerView.clearSelectedItem();
// practice_mode.setChecked(latestSettings.settings.get_practice_mode());
wifi_mode.setChecked(settingsManager.getWifiMode());
alliance_pos_spinnerView.selectItemByIndex(0);
team_num.setText(String.valueOf(settingsManager.getTeamNum()));
});
alert.setNegativeButton("Cancel", null);
alert.create().show();
}
});
return root; return root;
} }
@@ -235,4 +85,265 @@ public class settingsFragment extends Fragment {
super.onDestroyView(); super.onDestroyView();
binding = null; binding = null;
} }
public abstract class SettingsItem<T> {
private String key;
private String title;
private T defaultValue;
public SettingsItem(String key, String title, T defaultValue) {
this.key = key;
this.title = title;
this.defaultValue = defaultValue;
}
public abstract View createView(Context context);
public abstract T getValue();
public String getKey() { return key; }
public String getTitle() { return title; }
public T getDefaultValue() { return defaultValue; }
public abstract void setEnabled(boolean enabled);
}
public class StringSettingsItem extends SettingsItem<String> {
public StringSettingsItem(String key, String title) {
super(key, title, prefs.getString(key, (String) defaults.get(key)));
}
TextInputEditText editText;
@Override
public void setEnabled(boolean enabled){
editText.setEnabled(enabled);
}
@Override
public View createView(Context context) {
TextInputLayout textInputLayout = new TextInputLayout(context);
textInputLayout.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
));
editText = new TextInputEditText(context);
editText.setText(getValue());
editText.addTextChangedListener(new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
getEditor().putString(getKey(), s.toString()).apply();
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}
});
textInputLayout.addView(editText);
return textInputLayout;
}
@Override
public String getValue() {
return prefs.getString(getKey(), (String) defaults.get(getKey()));
}
}
public class NumberSettingsItem extends SettingsItem<Integer> {
private int min;
private int max;
public NumberSettingsItem(String key, String title, int min, int max) {
super(key, title, prefs.getInt(key, (int) defaults.get(key)));
this.min = min;
this.max = max;
}
TextInputEditText editText;
@Override
public void setEnabled(boolean enabled){
editText.setEnabled(enabled);
}
@Override
public View createView(Context context) {
TextView titleView = new TextView(context);
titleView.setText(getTitle());
titleView.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Subtitle1);
TextInputLayout textInputLayout = new TextInputLayout(context);
editText = new TextInputEditText(context);
editText.setInputType(InputType.TYPE_CLASS_NUMBER);
editText.setText(String.valueOf(getValue()));
editText.addTextChangedListener(new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
try {
int value = Integer.parseInt(s.toString());
if (value >= min && value <= max) {
getEditor().putInt(getKey(), value).apply();
}
} catch (NumberFormatException e) {
editText.setText(String.valueOf(getDefaultValue()));
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}
});
textInputLayout.addView(editText);
textInputLayout.addView(titleView);
return textInputLayout;
}
@Override
public Integer getValue() {
return prefs.getInt(getKey(), (int) defaults.get(getKey()));
}
}
public class DropdownSettingsItem extends SettingsItem<String> {
private String[] options;
private boolean enabled = true;
@Override
public void setEnabled(boolean enabled){
this.enabled = enabled;
}
public DropdownSettingsItem(String key, String title, String[] options) {
super(key, title, prefs.getString(key, (String) defaults.get(key)));
this.options = options;
}
@Override
public View createView(Context context) {
CustomSpinnerView dropdown = new CustomSpinnerView(getContext());
dropdown.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
ArrayList<String> optionsList = new ArrayList<>(Arrays.asList(options));
dropdown.setTitle(getTitle());
dropdown.setOptions(optionsList, getValue());
dropdown.setOption(getValue());
dropdown.setOnClickListener((item, index) -> {
getEditor().putString(getKey(), item).apply();
});
return dropdown;
}
@Override
public String getValue() {
return prefs.getString(getKey(), (String) defaults.get(getKey()));
}
}
public class CheckboxSettingsItem extends SettingsItem<Boolean> {
private List<SettingsItem<?>> controlledItems;
public CheckboxSettingsItem(String key, String title, SettingsItem<?>... controlledItems) {
super(key, title, prefs.getBoolean(key, (Boolean) defaults.get(key)));
this.controlledItems = Arrays.asList(controlledItems);
}
MaterialCheckBox checkBox;
@Override
public void setEnabled(boolean enabled){
checkBox.setEnabled(enabled);
for (SettingsItem<?> item : controlledItems) {
item.setEnabled(enabled && checkBox.isChecked());
}
}
@Override
public View createView(Context context) {
checkBox = new MaterialCheckBox(context);
checkBox.setText(getTitle());
checkBox.setChecked(getValue());
checkBox.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Subtitle1);
checkBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
getEditor().putBoolean(getKey(), isChecked).apply();
for (SettingsItem<?> item : controlledItems) {
item.setEnabled(isChecked);
}
});
for (SettingsItem<?> item : controlledItems) {
item.setEnabled(getValue());
}
return checkBox;
}
@Override
public Boolean getValue() {
return prefs.getBoolean(getKey(), (Boolean) defaults.get(getKey()));
}
}
public class SettingsManager {
private Context context;
private HashMap<String, Object> settings;
private List<SettingsItem<?>> items;
// private LinearLayout container;
public SettingsManager(Context context) {
this.context = context;
this.items = new ArrayList<>();
// this.container = new LinearLayout(context);
// this.container.setOrientation(LinearLayout.VERTICAL);
// this.container.setLayoutParams(new LinearLayout.LayoutParams(
// LinearLayout.LayoutParams.MATCH_PARENT,
// LinearLayout.LayoutParams.WRAP_CONTENT
// ));
}
private final List<View> views = new ArrayList<>();
public void addItem(SettingsItem<?> item) {
items.add(item);
LinearLayout itemContainer = new LinearLayout(context);
itemContainer.setOrientation(LinearLayout.VERTICAL);
itemContainer.setPadding(32, 0, 32, 8);
itemContainer.addView(item.createView(context));
views.add(itemContainer);
}
public void getView(LinearLayout layout) {
for(int i = views.size()-1; i >= 0; i--)
layout.addView(views.get(i));
}
}
} }
@@ -12,12 +12,16 @@ import com.ridgebotics.ridgescout.scoutingData.ScoutingDataWriter;
import com.ridgebotics.ridgescout.types.data.dataType; import com.ridgebotics.ridgescout.types.data.dataType;
import com.ridgebotics.ridgescout.types.frcMatch; import com.ridgebotics.ridgescout.types.frcMatch;
import com.ridgebotics.ridgescout.types.frcTeam; import com.ridgebotics.ridgescout.types.frcTeam;
import com.ridgebotics.ridgescout.types.input.inputType;
import com.ridgebotics.ridgescout.utility.DataManager; import com.ridgebotics.ridgescout.utility.DataManager;
import com.ridgebotics.ridgescout.utility.fileEditor; import com.ridgebotics.ridgescout.utility.fileEditor;
public class CSVExport { public class CSVExport {
private static String[] alliances = {"red", "blue"}; private static String[] alliances = {"red", "blue"};
private static String safeCSV(String input){
return input.replace("\n", "").replace(",", ".").replace(";", ".");
}
public static void exportMatches(Context c){ public static void exportMatches(Context c){
DataManager.reload_event(); DataManager.reload_event();
@@ -55,10 +59,18 @@ public class CSVExport {
if(!fileEditor.fileExist(filename)){ if(!fileEditor.fileExist(filename)){
data += ("null,".repeat(match_latest_values.length)); data += ("null,".repeat(match_latest_values.length));
}else{ }else{
try {
String tempData = "";
ScoutingDataWriter.ParsedScoutingDataResult psdr = ScoutingDataWriter.load(filename, DataManager.match_values, DataManager.match_transferValues); ScoutingDataWriter.ParsedScoutingDataResult psdr = ScoutingDataWriter.load(filename, DataManager.match_values, DataManager.match_transferValues);
dataType[] types = psdr.data.array; dataType[] matchData = psdr.data.array;
inputType[] types = psdr.data.values[psdr.data.values.length-1];
for (int i = 0; i < types.length; i++) { for (int i = 0; i < types.length; i++) {
data += (types[i].get() + ","); tempData += (safeCSV(types[i].toString(matchData[i])) + ",");
}
data += tempData;
} catch (Exception e){
e.printStackTrace();
data += ("null,".repeat(pit_latest_values.length));
} }
} }
@@ -77,7 +89,7 @@ public class CSVExport {
String data = ""; String data = "";
data += ("teamnum,teamname,city,teamnum,stateOrProv,school,country,startingYear,"); data += ("teamnum,teamname,city,stateOrProv,school,country,startingYear,");
for(int i = 0; i < pit_latest_values.length; i++){ for(int i = 0; i < pit_latest_values.length; i++){
data += (pit_latest_values[i].name + ","); data += (pit_latest_values[i].name + ",");
} }
@@ -99,10 +111,18 @@ public class CSVExport {
if(!fileEditor.fileExist(filename)){ if(!fileEditor.fileExist(filename)){
data += ("null,".repeat(pit_latest_values.length)); data += ("null,".repeat(pit_latest_values.length));
}else{ }else{
try {
String tempData = "";
ScoutingDataWriter.ParsedScoutingDataResult psdr = ScoutingDataWriter.load(filename, DataManager.pit_values, DataManager.pit_transferValues); ScoutingDataWriter.ParsedScoutingDataResult psdr = ScoutingDataWriter.load(filename, DataManager.pit_values, DataManager.pit_transferValues);
dataType[] types = psdr.data.array; dataType[] teamData = psdr.data.array;
inputType[] types = psdr.data.values[psdr.data.values.length-1];
for (int i = 0; i < types.length; i++) { for (int i = 0; i < types.length; i++) {
data += (types[i].get() + ","); tempData += (safeCSV(types[i].toString(teamData[i])) + ",");
}
data += tempData;
} catch (Exception e){
e.printStackTrace();
data += ("null,".repeat(pit_latest_values.length));
} }
} }
@@ -0,0 +1,275 @@
package com.ridgebotics.ridgescout.ui.transfer;
//import static com.ridgebotics.ridgescout.utility.DataManager.evcode;
import static com.ridgebotics.ridgescout.utility.DataManager.evcode;
import static com.ridgebotics.ridgescout.utility.fileEditor.baseDir;
import com.ridgebotics.ridgescout.ui.data.FieldEditorHelper;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder;
import com.ridgebotics.ridgescout.utility.fileEditor;
import com.ridgebotics.ridgescout.utility.settingsManager;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.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.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
public class FTPSync extends Thread {
public static final String remoteBasePath = "/RidgeScout/";
public static final String timestampsFilename = "timestamps";
public static long lastSyncTime = 0;
private static Date curSyncTime;
private static final long millisTolerance = 1000;
private boolean after(Date a, Date b){
return a.getTime() - b.getTime() > millisTolerance;
}
public interface onResult {
void onResult(boolean error, int upCount, int downCount);
}
public interface UpdateIndicator {
void onText(String text);
}
private static UpdateIndicator updateIndicator = text -> {};
public static String text = "";
private static void setUpdateIndicator(String m_text){
text = m_text;
updateIndicator.onText(m_text);
}
public static void setOnUpdateIndicator(UpdateIndicator m_updateIndicator){
updateIndicator = m_updateIndicator;
}
private static onResult onResult = (error, upCount, downCount) -> {};
public static void setOnResult(onResult result){
onResult = result;
}
private static boolean isRunning = false;
public static boolean getIsRunning(){return isRunning;}
public static void sync(){
// DataManager.reload_event();
FTPSync ftpSync = new FTPSync();
curSyncTime = new Date();
ftpSync.start();
}
FTPClient ftpClient;
private int upCount = 0;
private int downCount = 0;
private void downloadFile(String remoteFile, File localFile) throws IOException {
try (FileOutputStream fos = new FileOutputStream(localFile)) {
ftpClient.retrieveFile(remoteBasePath + remoteFile, fos);
}
}
private void uploadFile(File localFile) throws IOException {
try (FileInputStream fis = new FileInputStream(localFile)) {
ftpClient.storeFile(remoteBasePath + localFile.getName(), fis);
}
}
private FTPFile findRemoteFile(FTPFile[] remoteFiles, String fileName) {
for (FTPFile file : remoteFiles) {
if (file.getName().equals(fileName)) {
return file;
}
}
return null;
}
private Date getUtcTimestamp(FTPFile file) {
return file.getTimestamp().getTime();
}
private Date getLocalFileUtcTimestamp(File file) {
return new Date(file.lastModified());
}
private void setLocalFileTimestamp(File file, Date date) {
file.setLastModified(date.getTime());
}
public void run() {
isRunning = true;
boolean sendMetaFiles = settingsManager.getFTPSendMetaFiles();
// Meta files
List<String> meta_string_array = Arrays.asList(
"matches.fields",
"pits.fields",
evcode+".eventdata"
);
try {
// Login to FTP
ftpClient = new FTPClient();
InetAddress address = InetAddress.getByName(settingsManager.getFTPServer());
ftpClient.connect(address);
ftpClient.login("anonymous", null);
ftpClient.enterLocalPassiveMode();
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
File localDir = new File(baseDir);
File[] localFiles = localDir.listFiles();
Map<String, Date> remoteTimestamps = getTimestamps();
// Loop through local files and send all that are more recent
if (localFiles != null) {
for (int i = 0; i < localFiles.length; i++) {
File localFile = localFiles[i];
setUpdateIndicator("Uploading " + (i+1) + "/" + localFiles.length);
if(localFile.isDirectory()) continue;
// Remove timestamts file
if(localFile.getName().equals(timestampsFilename)) continue;
// Remove meta files if the option is disabled
if(!sendMetaFiles && meta_string_array.contains(localFile.getName())) continue;
Date remoteTimestamp = remoteTimestamps.get(localFile.getName());
Date localTimeStamp = getLocalFileUtcTimestamp(localFile);
if (remoteTimestamp == null || after(localTimeStamp, remoteTimestamp)) {
uploadFile(localFile);
System.out.println("Uploaded" + localFile.getName());
setLocalFileTimestamp(localFile, curSyncTime);
remoteTimestamps.put(localFile.getName(), curSyncTime);
upCount++;
}else{
System.out.println("Did not upload");
}
}
}
Set<String> keySet = remoteTimestamps.keySet();
Iterator<String> keyIt = keySet.iterator();
for (int i = 0; i < keySet.size(); i++) {
String remoteFile = keyIt.next();
setUpdateIndicator("Downloading " + (i+1) + "/" + keySet.size());
File localFile = new File(baseDir, remoteFile);
if(remoteFile.equals(timestampsFilename)) continue;
// Remove meta files if the option is disabled
if(!sendMetaFiles && meta_string_array.contains(remoteFile)) continue;
// Date t1 = getLocalFileUtcTimestamp(localFile);
// Date t2 = getUtcTimestamp(remoteFile);
////
// System.out.println("- " + t1 + (t1.after(t2) ? ">" : "<") + t2);
Date localTimeStamp = getLocalFileUtcTimestamp(localFile);
Date remoteTimestamp = remoteTimestamps.get(remoteFile);
if (!localFile.exists() || (after(remoteTimestamp, localTimeStamp) && !localTimeStamp.equals(remoteTimestamp))) {
downloadFile(remoteFile, localFile);
System.out.println("Downloaded " + localFile.getName());
if(!localFile.exists()) System.out.println("Not exist");
else if(after(remoteTimestamp, localTimeStamp)) System.out.println("Before: " + (localTimeStamp.getTime()-remoteTimestamp.getTime()));
// Date d = getUtcTimestamp(remoteFile);
setLocalFileTimestamp(localFile, remoteTimestamps.get(localFile.getName()));
// remoteTimestamps.put(remoteFile, curSyncTime);
downCount++;
}else{
System.out.println("Did not download");
}
}
setTimestamps(remoteTimestamps);
} catch (Exception e) {
AlertManager.error(e);
onResult.onResult(true, upCount, downCount);
setUpdateIndicator("ERROR!");
} finally {
onResult.onResult(false, upCount, downCount);
setUpdateIndicator("Finished");
}
isRunning = false;
}
private boolean setTimestamps(Map<String, Date> timestamps){
try {
ByteBuilder bb = new ByteBuilder();
String[] filenames = timestamps.keySet().toArray(new String[0]);
for(int i = 0; i < filenames.length; i++){
bb.addString(filenames[i]);
bb.addLong(timestamps.get(filenames[i]).getTime());
}
fileEditor.writeFile(timestampsFilename, bb.build());
uploadFile(new File(baseDir + timestampsFilename));
return true;
} catch (ByteBuilder.buildingException | IOException e) {
e.printStackTrace();
return false;
}
}
private Map<String, Date> getTimestamps() {
try {
downloadFile(timestampsFilename, new File(baseDir + timestampsFilename));
byte[] data = fileEditor.readFile(timestampsFilename);
if(data == null || data.length == 0)
return new HashMap<>();
BuiltByteParser bbp = new BuiltByteParser(data);
List<BuiltByteParser.parsedObject> pa = bbp.parse();
Map<String, Date> output = new HashMap<>();
for(int i = 0; i < pa.size(); i+=2){
// System.out.println((long) pa.get(i).get());
output.put(
(String) pa.get(i).get(),
new Date((long) pa.get(i+1).get())
);
}
return output;
}catch (IOException | BuiltByteParser.byteParsingExeption e){
AlertManager.error(e);
return new HashMap<>();
}
}
}
@@ -6,6 +6,7 @@ import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.TableLayout; import android.widget.TableLayout;
import android.widget.TableRow; import android.widget.TableRow;
@@ -19,6 +20,7 @@ import com.ridgebotics.ridgescout.databinding.FragmentTransferFileSelectorBindin
import com.ridgebotics.ridgescout.types.file; import com.ridgebotics.ridgescout.types.file;
import com.ridgebotics.ridgescout.utility.AlertManager; import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.ByteBuilder; import com.ridgebotics.ridgescout.utility.ByteBuilder;
import com.ridgebotics.ridgescout.utility.DataManager;
import com.ridgebotics.ridgescout.utility.fileEditor; import com.ridgebotics.ridgescout.utility.fileEditor;
import java.util.ArrayList; import java.util.ArrayList;
@@ -41,6 +43,7 @@ public class FileSelectorFragment extends Fragment {
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
binding = FragmentTransferFileSelectorBinding.inflate(inflater, container, false); binding = FragmentTransferFileSelectorBinding.inflate(inflater, container, false);
DataManager.reload_event();
meta_string_array = new String[]{ meta_string_array = new String[]{
"matches.fields", "matches.fields",
@@ -66,6 +69,10 @@ public class FileSelectorFragment extends Fragment {
tr.setBackgroundColor(background_color); tr.setBackgroundColor(background_color);
CheckBox checkBox = new CheckBox(getContext());
checkBox.setChecked(true);
tr.addView(checkBox);
TextView tv = new TextView(getContext()); TextView tv = new TextView(getContext());
tv.setText(String.valueOf(files[i])); tv.setText(String.valueOf(files[i]));
tv.setTextSize(20); tv.setTextSize(20);
@@ -77,6 +84,7 @@ public class FileSelectorFragment extends Fragment {
selected_arr[fi] = sel; selected_arr[fi] = sel;
tr.setBackgroundColor(sel ? background_color : unselected_background_color); tr.setBackgroundColor(sel ? background_color : unselected_background_color);
((CheckBox) tr.getChildAt(0)).setChecked(sel);
}); });
} }
@@ -88,9 +96,12 @@ public class FileSelectorFragment extends Fragment {
Arrays.fill(selected_arr, Boolean.TRUE); Arrays.fill(selected_arr, Boolean.TRUE);
for(int i = 0; i < files.length; i++){ for(int i = 0; i < files.length; i++){
View child = binding.fileSelectorTable.getChildAt(i); TableRow child = (TableRow) binding.fileSelectorTable.getChildAt(i);
child.setBackgroundColor(background_color); child.setBackgroundColor(background_color);
child.setVisibility(is_in_search_param(files[i], search_param, match_num_nums) ? View.VISIBLE : View.GONE); boolean sel = is_in_search_param(files[i], search_param, match_num_nums);
child.setVisibility(sel ? View.VISIBLE : View.GONE);
((CheckBox) child.getChildAt(0)).setChecked(sel);
} }
return false; return false;
@@ -103,6 +114,7 @@ public class FileSelectorFragment extends Fragment {
View child = binding.fileSelectorTable.getChildAt(i); View child = binding.fileSelectorTable.getChildAt(i);
if(child.getVisibility() == View.VISIBLE && selected_arr[i]) if(child.getVisibility() == View.VISIBLE && selected_arr[i])
filenames.add(files[i]); filenames.add(files[i]);
} }
onSelect.onSelect(get_bytes_of_filenames(filenames)); onSelect.onSelect(get_bytes_of_filenames(filenames));
}); });
@@ -22,6 +22,7 @@ import com.ridgebotics.ridgescout.types.frcMatch;
import com.ridgebotics.ridgescout.types.frcTeam; import com.ridgebotics.ridgescout.types.frcTeam;
import com.ridgebotics.ridgescout.utility.fileEditor; import com.ridgebotics.ridgescout.utility.fileEditor;
import com.ridgebotics.ridgescout.utility.JSONUtil; import com.ridgebotics.ridgescout.utility.JSONUtil;
import com.ridgebotics.ridgescout.utility.settingsManager;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
@@ -35,13 +36,13 @@ import java.util.Comparator;
import java.util.Date; import java.util.Date;
public class TBAFragment extends Fragment { public class TBAFragment extends Fragment {
private final String TBAAddress = "https://www.thebluealliance.com/api/v3/"; private static final String TBAAddress = "https://www.thebluealliance.com/api/v3/";
private final String TBAHeader = "X-TBA-Auth-Key: tjEKSZojAU2pgbs2mBt06SKyOakVhLutj3NwuxLTxPKQPLih11aCIwRIVFXKzY4e"; private static final String TBAHeader = "X-TBA-Auth-Key: tjEKSZojAU2pgbs2mBt06SKyOakVhLutj3NwuxLTxPKQPLih11aCIwRIVFXKzY4e";
private android.widget.TableLayout Table; private android.widget.TableLayout Table;
private FragmentTransferTbaBinding binding; private FragmentTransferTbaBinding binding;
private static final int year = 2024; private final int year = settingsManager.getYearNum();
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) { @Nullable Bundle savedInstanceState) {
@@ -58,6 +59,10 @@ public class TBAFragment extends Fragment {
final RequestTask rq = new RequestTask(); final RequestTask rq = new RequestTask();
rq.onResult(s -> { rq.onResult(s -> {
if(s == null || s.isEmpty()) {
AlertManager.error("Could not fetch event!");
return null;
}
eventTable(s); eventTable(s);
return null; return null;
}); });
@@ -14,11 +14,16 @@ 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.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.settingsManager; 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 org.apache.commons.net.ftp.FTP;
import java.util.Date;
public class TransferFragment extends Fragment { public class TransferFragment extends Fragment {
private FragmentTransferBinding binding; private FragmentTransferBinding binding;
@@ -52,20 +57,38 @@ public class TransferFragment extends Fragment {
}); });
binding.TBAButton.setOnClickListener(v -> { binding.TBAButton.setOnClickListener(v -> {
binding.noEventError.setVisibility(View.GONE);
AlertDialog.Builder alert = new AlertDialog.Builder(getContext());
alert.setTitle("Warning");
alert.setMessage("This action requires internet.");
alert.setCancelable(true);
alert.setPositiveButton("Ok", (dialog, which) -> {
findNavController(this).navigate(R.id.action_navigation_transfer_to_navigation_tba); findNavController(this).navigate(R.id.action_navigation_transfer_to_navigation_tba);
}); });
alert.setNegativeButton("Cancel", null);
alert.create().show(); if(!settingsManager.getWifiMode()) {
binding.TBAButton.setEnabled(false);
binding.SyncButton.setEnabled(false);
}
if(!settingsManager.getFTPEnabled()) {
binding.SyncButton.setEnabled(false);
}
binding.SyncButton.setOnClickListener(v -> {
binding.SyncButton.setEnabled(false);
FTPSync.sync();
}); });
if(FTPSync.getIsRunning())
binding.SyncButton.setEnabled(false);
FTPSync.setOnResult((error, upcount, downcount) -> {
if (getActivity() != null)
getActivity().runOnUiThread(() -> {
binding.SyncButton.setEnabled(true);
AlertManager.toast((!error ? "Synced! " : "Error Syncing. ") + upcount + " Up " + downcount + " Down");
});
});
binding.syncIndicator.setText(FTPSync.text);
FTPSync.setOnUpdateIndicator(text -> {if(getActivity() != null) getActivity().runOnUiThread(() -> binding.syncIndicator.setText(text));});
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);
@@ -82,33 +105,15 @@ public class TransferFragment extends Fragment {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle("Chose data"); builder.setTitle("Chose data");
builder.setNegativeButton("Pit data", new DialogInterface.OnClickListener() { builder.setNegativeButton("Pit data", (dialog, which) -> CSVExport.exportPits(getContext()));
@Override
public void onClick(DialogInterface dialog, int which) {
CSVExport.exportPits(getContext());
}
});
builder.setPositiveButton("Match data", new DialogInterface.OnClickListener() { builder.setPositiveButton("Match data", (dialog, which) -> CSVExport.exportMatches(getContext()));
@Override
public void onClick(DialogInterface dialog, int which) {
CSVExport.exportMatches(getContext());
}
});
builder.setNeutralButton("Cancel", new DialogInterface.OnClickListener() { builder.setNeutralButton("Cancel", (dialog, which) -> dialog.cancel());
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
builder.show(); builder.show();
}); });
if(!settingsManager.getWifiMode())
binding.TBAButton.setEnabled(false);
return binding.getRoot(); return binding.getRoot();
} }
@@ -30,17 +30,11 @@ public class TransferSelectorFragment extends Fragment {
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
binding = FragmentTransferSelectorBinding.inflate(inflater, container, false); binding = FragmentTransferSelectorBinding.inflate(inflater, container, false);
binding.codesButton.setOnClickListener(view -> { binding.codesButton.setOnClickListener(view -> onselect.onSelectCodes(this));
onselect.onSelectCodes(this);
});
binding.bluetoothButton.setOnClickListener(view -> { binding.bluetoothButton.setOnClickListener(view -> onselect.onSelectBluetooth(this));
onselect.onSelectBluetooth(this);
});
binding.fileBundleButton.setOnClickListener(view -> { binding.fileBundleButton.setOnClickListener(view -> onselect.onSelectFileBundle(this));
onselect.onSelectFileBundle(this);
});
return binding.getRoot(); return binding.getRoot();
} }
@@ -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();
@@ -53,8 +53,7 @@ public class AlertManager {
public static void error(Exception e) { public static void error(Exception e) {
e.printStackTrace(); e.printStackTrace();
((Activity) context).runOnUiThread(new Runnable() { ((Activity) context).runOnUiThread(() -> {
public void run() {
StringWriter sw = new StringWriter(); StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw)); e.printStackTrace(new PrintWriter(sw));
@@ -65,7 +64,6 @@ public class AlertManager {
alert.setCancelable(true); alert.setCancelable(true);
alert.create().show(); alert.create().show();
}
}); });
} }
@@ -10,6 +10,7 @@ public class BuiltByteParser {
public static final Integer stringType = 2; public static final Integer stringType = 2;
public static final Integer intArrayType = 3; public static final Integer intArrayType = 3;
public static final Integer stringArrayType = 4; public static final Integer stringArrayType = 4;
public static final Integer longType = 5;
public class byteParsingExeption extends Exception { public class byteParsingExeption extends Exception {
public byteParsingExeption() {} public byteParsingExeption() {}
@@ -58,6 +59,11 @@ public class BuiltByteParser {
public Integer getType(){return stringArrayType;} public Integer getType(){return stringArrayType;}
public Object get(){return arr;} public Object get(){return arr;}
} }
public class longObject extends parsedObject{
long num;
public Integer getType(){return longType;}
public Object get(){return num;}
}
public class rawObject extends parsedObject { public class rawObject extends parsedObject {
@@ -140,6 +146,11 @@ public class BuiltByteParser {
sa.arr = StringArr; sa.arr = StringArr;
objects.add(sa); objects.add(sa);
break; break;
case 5:
longObject lo = new longObject();
lo.num = fileEditor.fromBytesLong(block, length);
objects.add(lo);
break;
default: default:
rawObject ro = new rawObject(type); rawObject ro = new rawObject(type);
ro.bytes = block; ro.bytes = block;
@@ -9,6 +9,7 @@ public class ByteBuilder {
public static final int string_id = 2; public static final int string_id = 2;
public static final int int_arr_id = 3; public static final int int_arr_id = 3;
public static final int string_arr_id = 4; public static final int string_arr_id = 4;
public static final int long_id = 5;
ArrayList<byteType> bytesToBuild = new ArrayList<>(); ArrayList<byteType> bytesToBuild = new ArrayList<>();
@@ -153,6 +154,41 @@ public class ByteBuilder {
return this; return this;
} }
private class longType extends byteType {
public int precision;
public long num;
public byte getType(){return long_id;}
public int length(){return precision;}
public byte[] build(){
return fileEditor.toBytes(num, precision);
}
}
private int getLeastBytePrecision(long num){
if(num <= 1){return 1;}
return (int) Math.ceil(Math.log(Math.abs(num))/Math.log(8));
}
public ByteBuilder addLong(long num) throws buildingException {
// Get closest number of bytes
int precision = getLeastBytePrecision(num);
return addLong(num, precision);
}
public ByteBuilder addLong(long num, int precision) throws buildingException {
if(precision <= 0){throw new buildingException("Invalid precision: " + precision);}
if(precision > 65536){throw new buildingException("Precision too large (greter than 65536)");}
if(precision < getLeastBytePrecision(num)){throw new buildingException("Precision too small");}
if(num > Long.MAX_VALUE){throw new buildingException("Long overflow");}
if(num < Long.MIN_VALUE){throw new buildingException("Long overflow");}
longType longType = new longType();
longType.num = num;
longType.precision = precision;
bytesToBuild.add(longType);
return this;
}
private class rawType extends byteType { private class rawType extends byteType {
public int type; public int type;
public byte[] bytes; public byte[] bytes;
@@ -10,7 +10,16 @@ public class DataManager {
public static frcEvent event; public static frcEvent event;
public static void reload_event(){ public static void reload_event(){
evcode = getevcode(); evcode = getevcode();
if(evcode.equals("unset")) return;
event = frcEvent.decode(fileEditor.readFile(evcode + ".eventdata")); event = frcEvent.decode(fileEditor.readFile(evcode + ".eventdata"));
if(event == null) {
AlertManager.error("Failed to load event!");
settingsManager.setEVCode("unset");
evcode = "unset";
}
} }
public static String getevcode() { public static String getevcode() {
@@ -21,17 +30,25 @@ public class DataManager {
public static inputType[] match_latest_values; public static inputType[] match_latest_values;
public static transferType[][] match_transferValues; public static transferType[][] match_transferValues;
public static void reload_match_fields(){ public static void reload_match_fields(){
try {
match_values = fields.load(fields.matchFieldsFilename); match_values = fields.load(fields.matchFieldsFilename);
match_latest_values = match_values[match_values.length - 1]; match_latest_values = match_values[match_values.length - 1];
match_transferValues = transferType.get_transfer_values(match_values); match_transferValues = transferType.get_transfer_values(match_values);
} catch (Exception e){
AlertManager.error(e);
}
} }
public static inputType[][] pit_values; public static inputType[][] pit_values;
public static inputType[] pit_latest_values; public static inputType[] pit_latest_values;
public static transferType[][] pit_transferValues; public static transferType[][] pit_transferValues;
public static void reload_pit_fields(){ public static void reload_pit_fields(){
try {
pit_values = fields.load(fields.pitsFieldsFilename); pit_values = fields.load(fields.pitsFieldsFilename);
pit_latest_values = pit_values[pit_values.length-1]; pit_latest_values = pit_values[pit_values.length-1];
pit_transferValues = transferType.get_transfer_values(pit_values); pit_transferValues = transferType.get_transfer_values(pit_values);
} catch (Exception e){
AlertManager.error(e);
}
} }
} }
@@ -15,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();
@@ -43,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);
} }
@@ -61,6 +66,17 @@ public final class fileEditor {
return bytes; return bytes;
} }
public static byte[] toBytes(long num, int byteCount){
if(num > (Math.pow(2,byteCount*8)-1)){
throw new BufferOverflowException();
}
byte[] bytes = new byte[byteCount];
for(int i=0;i<byteCount;i++){
bytes[i] = (byte)(num >> (i*8));
}
return bytes;
}
public static int fromBytes(byte[] bytes, int byteCount){ public static int fromBytes(byte[] bytes, int byteCount){
@@ -70,6 +86,13 @@ public final class fileEditor {
} }
return returnInt; return returnInt;
} }
public static long fromBytesLong(byte[] bytes, int byteCount){
long returnLong = 0;
for(int i=0;i<byteCount;i++){
returnLong |= (bytes[i] & 0xFFL) << (i*8);
}
return returnLong;
}
@@ -189,7 +212,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());
// }
// }
@@ -198,6 +236,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) {
@@ -341,9 +383,8 @@ public final class fileEditor {
String[] filenames = outFiles.toArray(new String[0]); String[] filenames = outFiles.toArray(new String[0]);
Arrays.sort(filenames, new Comparator<String>() { try {
@Override Arrays.sort(filenames, (o1, o2) -> {
public int compare(String o1, String o2) {
try { try {
if (!o1.contains("-") || !o2.contains("-")) if (!o1.contains("-") || !o2.contains("-"))
return 0; return 0;
@@ -351,8 +392,10 @@ public final class fileEditor {
} catch (Exception e) { } catch (Exception e) {
return 0; return 0;
} }
}
}); });
} catch (Exception e){
e.printStackTrace();
}
return filenames; return filenames;
@@ -1,89 +0,0 @@
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);
}
}
}
@@ -1,152 +0,0 @@
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;
}
}
@@ -13,12 +13,16 @@ public class settingsManager {
public static final String UnameKey = "username"; public static final String UnameKey = "username";
public static final String SelEVCodeKey = "selected_event_code"; public static final String SelEVCodeKey = "selected_event_code";
public static final String YearNumKey = "year_num";
public static final String WifiModeKey = "wifi_mode"; 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 MatchNumKey = "match_num";
public static final String AllyPosKey = "alliance_pos"; public static final String AllyPosKey = "alliance_pos";
public static final String DataModeKey = "data_view_mode"; public static final String DataModeKey = "data_view_mode";
public static final String BtUUIDKey = "bt_uuid"; public static final String BtUUIDKey = "bt_uuid";
public static final String FTPEnabled = "ftp_enabled";
public static final String FTPServer = "ftp_server";
public static final String FTPSendMetaFiles = "ftp_send_meta_files";
public static final String CustomEventsKey = "enable_custom_event";
public static Map defaults = getDefaults(); public static Map defaults = getDefaults();
private static Map getDefaults(){ private static Map getDefaults(){
@@ -27,20 +31,43 @@ public class settingsManager {
hm.put(UnameKey, "Username"); hm.put(UnameKey, "Username");
hm.put(SelEVCodeKey, "unset"); hm.put(SelEVCodeKey, "unset");
hm.put(WifiModeKey, false); hm.put(WifiModeKey, false);
hm.put(TeamNumKey, 4388); hm.put(YearNumKey, 2025);
hm.put(MatchNumKey, 0); hm.put(MatchNumKey, 0);
hm.put(AllyPosKey, "red-1"); hm.put(AllyPosKey, "red-1");
hm.put(DataModeKey, 0); hm.put(DataModeKey, 0);
hm.put(BtUUIDKey, UUID.randomUUID().toString()); hm.put(BtUUIDKey, UUID.randomUUID().toString());
hm.put(FTPEnabled, false);
hm.put(FTPServer, "0.0.0.0");
hm.put(FTPSendMetaFiles, false);
hm.put(CustomEventsKey, false);
return hm; return hm;
} }
private static SharedPreferences.Editor getEditor(){ public static SharedPreferences.Editor getEditor(){
if(editor == null) editor = prefs.edit(); if(editor == null) editor = prefs.edit();
return editor; 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(YearNumKey, (int) defaults.get( YearNumKey )).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( FTPServer )).apply();
getEditor().putBoolean(FTPSendMetaFiles, (boolean) defaults.get( FTPSendMetaFiles )).apply();
getEditor().putBoolean(CustomEventsKey, (boolean) defaults.get( CustomEventsKey )).apply();
}
// IDK why I decided to format these functions like this. It looks cool though. // 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 String getUsername(){return prefs.getString( UnameKey, (String) defaults.get(UnameKey));}
public static void setUsername(String str){ getEditor().putString( UnameKey,str).apply();} public static void setUsername(String str){ getEditor().putString( UnameKey,str).apply();}
@@ -51,8 +78,8 @@ public class settingsManager {
public static boolean getWifiMode(){return prefs.getBoolean( WifiModeKey, (boolean) defaults.get(WifiModeKey));} 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 void setWifiMode(boolean bool){getEditor().putBoolean( WifiModeKey,bool).apply();}
public static int getTeamNum(){return prefs.getInt( TeamNumKey, (int) defaults.get(TeamNumKey));} public static int getYearNum(){return prefs.getInt( YearNumKey, (int) defaults.get(YearNumKey));}
public static void setTeamNum(int num){ getEditor().putInt( TeamNumKey,num).apply();} public static void setYearNum(int num){ getEditor().putInt( YearNumKey,num).apply();}
public static int getMatchNum(){return prefs.getInt( MatchNumKey, (int) defaults.get(MatchNumKey));} public static int getMatchNum(){return prefs.getInt( MatchNumKey, (int) defaults.get(MatchNumKey));}
public static void setMatchNum(int num){ getEditor().putInt( MatchNumKey,num).apply();} public static void setMatchNum(int num){ getEditor().putInt( MatchNumKey,num).apply();}
@@ -66,4 +93,22 @@ public class settingsManager {
public static String getBtUUID(){return prefs.getString( BtUUIDKey, (String) defaults.get(BtUUIDKey));} 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 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();}
public static boolean getFTPSendMetaFiles(){return prefs.getBoolean(FTPSendMetaFiles, (boolean) defaults.get(FTPSendMetaFiles));}
public static void setFTPSendMetaFiles(boolean bool){getEditor().putBoolean(FTPSendMetaFiles,bool).apply();}
public static boolean getCustomEvents(){return prefs.getBoolean(CustomEventsKey, (boolean) defaults.get(FTPSendMetaFiles));}
public static void setCustomEvents(boolean bool){getEditor().putBoolean(CustomEventsKey,bool).apply();}
} }
Binary file not shown.

After

Width:  |  Height:  |  Size: 997 B

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/background"
android:tileMode="repeat" />
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@android:color/transparent"/>
<stroke android:width="2dip" android:color="#626262" />
<corners android:radius="3dip"/>
<padding android:left="0dip"
android:top="0dip"
android:right="0dip"
android:bottom="0dip" />
</shape>
+5
View File
@@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="12dp" android:viewportHeight="24" android:viewportWidth="24" android:width="12dp">
<path android:fillColor="#000000" android:fillType="evenOdd" android:pathData="M5,8l7,8l7,-8z" android:strokeColor="#00000000" android:strokeWidth="1"/>
</vector>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

@@ -4,6 +4,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container" android:id="@+id/container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:background="@drawable/background_repeat"
android:layout_height="match_parent"> android:layout_height="match_parent">
<fragment <fragment
+23 -16
View File
@@ -44,33 +44,40 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Teams" android:text="Teams"
android:textSize="34sp" android:textSize="34sp"
app:layout_constraintBottom_toTopOf="@id/compareButton" 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/fieldsButton" /> app:layout_constraintTop_toBottomOf="@id/fieldsButton" />
<Button <LinearLayout
android:id="@+id/compareButton" android:id="@+id/fieldsButtons"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Compare" android:layout_marginTop="1dp"
android:textSize="34sp" android:gravity="center"
app:layout_constraintBottom_toTopOf="@+id/reportButton" android:orientation="horizontal"
android:visibility="gone"
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/teamsButton" /> app:layout_constraintTop_toBottomOf="@+id/fieldsButton"
tools:visibility="visible">
<Button <Button
android:id="@+id/reportButton" android:id="@+id/fieldsPitsButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Report" android:layout_margin="5dp"
android:textSize="34sp" android:text="Pits"
app:layout_constraintBottom_toBottomOf="parent" android:textSize="34sp" />
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" <Button
app:layout_constraintTop_toBottomOf="@+id/compareButton" android:id="@+id/fieldsMatchesButton"
app:layout_constraintVertical_bias="0.689" /> android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:text="Matches"
android:textSize="34sp" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TBD"
android:textSize="34sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,59 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TableLayout
android:layout_width="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>
</ScrollView>
@@ -1,30 +0,0 @@
<?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>
+118 -6
View File
@@ -1,8 +1,14 @@
<?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" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/teamsMainElem"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
xmlns:app="http://schemas.android.com/apk/res-auto">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -12,13 +18,119 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
<ScrollView <com.ridgebotics.ridgescout.ui.CustomSpinnerView
android:id="@+id/teamsArea" android:id="@+id/data_type_spinner"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="wrap_content" />
</ScrollView> <TextView
android:id="@+id/team_name2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView"
android:textAlignment="center"
android:textSize="24sp"
app:layout_constraintTop_toTopOf="parent"
tools:layout_editor_absoluteX="0dp" />
<TextView
android:id="@+id/team_description2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView"
android:textAlignment="center"
app:layout_constraintTop_toBottomOf="@+id/team_name2"
tools:layout_editor_absoluteX="0dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Pit Data"
android:textAlignment="center"
android:textSize="24sp"
app:layout_constraintTop_toBottomOf="@+id/team_description2"
tools:layout_editor_absoluteX="0dp" />
<LinearLayout
android:id="@+id/pitArea"
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">
</LinearLayout> </LinearLayout>
<TextView
android:id="@+id/textView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Match Data"
android:textAlignment="center"
android:textSize="24sp"
app:layout_constraintTop_toBottomOf="@+id/team_description2"
tools:layout_editor_absoluteX="0dp" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/individual_view_selector"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:visibility="gone"
tools:visibility="visible">
<Button
android:id="@+id/matches_minus_btn"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:padding="0dp"
android:text="@string/back"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/match_num"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/match_num"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="0"
android:textAlignment="gravity"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline4"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:layout_editor_absoluteY="3dp" />
<Button
android:id="@+id/matches_plus_btn"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:padding="0dp"
android:text="next"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/match_num"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout
android:id="@+id/matchArea"
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">
</LinearLayout>
</LinearLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
+17 -5
View File
@@ -50,23 +50,35 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/pit_n_scouting" android:text="@string/pit_n_scouting"
android:textSize="34sp" android:textSize="34sp"
app:layout_constraintBottom_toTopOf="@id/status_button" app:layout_constraintBottom_toTopOf="@id/event_button"
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/matchScoutingButton" /> app:layout_constraintTop_toBottomOf="@id/matchScoutingButton" />
<Button <Button
android:id="@+id/status_button" android:id="@+id/event_button"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Status" android:text="event"
android:textSize="34sp" android:textSize="34sp"
app:layout_constraintTop_toBottomOf="@+id/pitScoutingButton" 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_constraintBottom_toBottomOf="parent" app:layout_constraintTop_toBottomOf="@+id/pitScoutingButton"
app:layout_constraintVertical_bias="0.307" /> app:layout_constraintVertical_bias="0.307" />
<Button
android:id="@+id/event_add_button"
android:layout_width="58dp"
android:layout_height="63dp"
android:layout_marginStart="4dp"
android:text="+"
android:textSize="24sp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/event_button"
app:layout_constraintTop_toBottomOf="@+id/pitScoutingButton" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
@@ -0,0 +1,132 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<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">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center">
<Button
android:id="@+id/teams_minus_btn"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:padding="0dp"
android:text="del"
android:textSize="20sp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/textView3"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Teams"
android:textAlignment="gravity"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline4"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/teams_plus_btn"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:padding="0dp"
android:text="Add"
android:textSize="20sp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/textView3"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>
<TableLayout
android:id="@+id/teamsTable"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</TableLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:gravity="center"
android:layout_height="wrap_content"
android:layout_width="match_parent">
<Button
android:id="@+id/matches_minus_btn"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:padding="0dp"
android:text="del"
android:textSize="20sp"
android:visibility="gone"
app:layout_constraintEnd_toStartOf="@+id/textView4"
app:layout_constraintStart_toStartOf="parent"
tools:layout_editor_absoluteY="1dp"
tools:visibility="visible" />
<TextView
android:id="@+id/textView4"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="Matches"
android:textAlignment="gravity"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline4"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/matches_plus_btn"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:padding="0dp"
android:text="Add"
android:textSize="20sp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.686"
app:layout_constraintStart_toEndOf="@+id/textView4"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>
<TableLayout
android:id="@+id/matchTable"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</TableLayout>
</LinearLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,28 +0,0 @@
<?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">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<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">
<TableLayout
android:id="@+id/matchTable"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
+3 -167
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,14 @@
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"> 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>
+31 -9
View File
@@ -27,40 +27,53 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
<Button <com.google.android.material.button.MaterialButton
android:id="@+id/uploadButton" android:id="@+id/uploadButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
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" />
<Button <com.google.android.material.button.MaterialButton
android:id="@+id/downloadButton" android:id="@+id/downloadButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
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 <com.google.android.material.button.MaterialButton
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" />
<com.google.android.material.button.MaterialButton
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
<com.google.android.material.button.MaterialButton
android:id="@+id/TBAButton" android:id="@+id/TBAButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -69,7 +82,16 @@
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" />
<TextView
android:id="@+id/sync_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/SyncButton" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
@@ -16,7 +16,6 @@
<ScrollView <ScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginBottom="53dp"
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"
@@ -41,6 +40,7 @@
android:layout_height="48dp" android:layout_height="48dp"
android:background="#D33D3D3D" android:background="#D33D3D3D"
android:ems="10" android:ems="10"
android:hint="Search"
android:inputType="text" android:inputType="text"
android:textColor="#FFFFFF" android:textColor="#FFFFFF"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="3dp"
android:orientation="horizontal"
android:background="@drawable/border"
tools:ignore="UselessParent">
<TextView
android:id="@+id/item"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp"
android:textSize="24sp"
android:overlapAnchor="false"/>
</LinearLayout>
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="9dp"
android:layout_marginTop="-5dp"
android:paddingLeft="3dp"
android:paddingRight="3dp"
android:text="Test"
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"/>
</RelativeLayout>
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="3dp"
android:orientation="horizontal"
android:background="@drawable/border"
tools:ignore="UselessParent">
<TextView
android:id="@+id/item"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp"
android:textSize="24sp"
android:overlapAnchor="false"/>
</LinearLayout>
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="9dp"
android:layout_marginTop="-5dp"
android:paddingLeft="3dp"
android:paddingRight="3dp"
android:text="Test"
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"/>
</RelativeLayout>
@@ -8,6 +8,7 @@
android:id="@+id/minus_button" android:id="@+id/minus_button"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
android:text="-" /> android:text="-" />
<TextView <TextView
@@ -22,6 +23,7 @@
android:id="@+id/plus_button" android:id="@+id/plus_button"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
android:text="+" /> android:text="+" />
</LinearLayout> </LinearLayout>
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/toggle_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView"
android:textAlignment="center"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<CheckBox
android:id="@+id/toggle_title_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/toggle_title_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Description"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toggle_title"
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption"/>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -2,4 +2,5 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/> <background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/> <foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon> </adaptive-icon>
@@ -2,4 +2,5 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/> <background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/> <foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_robologo" />
</adaptive-icon> </adaptive-icon>
@@ -15,21 +15,21 @@
app:destination="@id/navigation_match_scouting" app:destination="@id/navigation_match_scouting"
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/pop_enter_anim"
app:popExitAnim="@anim/pop_exit_anim"/> app:popExitAnim="@anim/pop_exit_anim"/>
<action <action
android:id="@+id/action_navigation_scouting_to_navigation_team_selector" android:id="@+id/action_navigation_scouting_to_navigation_team_selector"
app:destination="@id/navigation_team_selector" app:destination="@id/navigation_team_selector"
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/pop_enter_anim"
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_event"
app:destination="@id/navigation_scouting_status" app:destination="@id/navigation_scouting_status"
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/pop_enter_anim"
app:popExitAnim="@anim/pop_exit_anim"/> app:popExitAnim="@anim/pop_exit_anim"/>
</fragment> </fragment>
@@ -48,14 +48,14 @@
app:destination="@id/navigation_pit_scouting" app:destination="@id/navigation_pit_scouting"
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/pop_enter_anim"
app:popExitAnim="@anim/pop_exit_anim" /> app:popExitAnim="@anim/pop_exit_anim" />
<action <action
android:id="@+id/action_navigation_team_selector_to_navigation_data_teams" android:id="@+id/action_navigation_team_selector_to_navigation_data_teams"
app:destination="@id/navigation_data_teams" app:destination="@id/navigation_data_teams"
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/pop_enter_anim"
app:popExitAnim="@anim/pop_exit_anim" /> app:popExitAnim="@anim/pop_exit_anim" />
</fragment> </fragment>
@@ -79,35 +79,21 @@
app:destination="@id/navigation_team_selector" app:destination="@id/navigation_team_selector"
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/pop_enter_anim"
app:popExitAnim="@anim/pop_exit_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"
app:destination="@id/navigation_data_fields_chooser" app:destination="@id/navigation_data_fields"
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/pop_enter_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
android:id="@+id/action_navigation_data_to_navigation_data_compare"
app:destination="@id/navigation_data_compare"
app:enterAnim="@anim/enter_anim"
app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/pop_exit_anim" /> app:popExitAnim="@anim/pop_exit_anim" />
</fragment> </fragment>
<fragment <fragment
android:id="@+id/navigation_scouting_status" android:id="@+id/navigation_scouting_status"
android:name="com.ridgebotics.ridgescout.ui.scouting.StatusFragment" android:name="com.ridgebotics.ridgescout.ui.scouting.EventFragment"
tools:layout="@layout/fragment_scouting_status"> tools:layout="@layout/fragment_scouting_event">
</fragment> </fragment>
<fragment <fragment
@@ -116,60 +102,19 @@
tools:layout="@layout/fragment_data_teams"> tools:layout="@layout/fragment_data_teams">
</fragment> </fragment>
<fragment
android:id="@+id/navigation_data_compare"
android:name="com.ridgebotics.ridgescout.ui.data.CompareFragment"
tools:layout="@layout/fragment_data_report">
</fragment>
<fragment
android:id="@+id/navigation_data_report"
android:name="com.ridgebotics.ridgescout.ui.data.ReportFragment"
tools:layout="@layout/fragment_data_report">
</fragment>
<fragment
android:id="@+id/navigation_data_fields_chooser"
android:name="com.ridgebotics.ridgescout.ui.data.FieldsChooserFragment"
tools:layout="@layout/fragment_data_fields_chooser">
<action
android:id="@+id/action_navigation_data_fields_chooser_to_navigation_data_fields"
app:destination="@id/navigation_data_fields"
app:enterAnim="@anim/enter_anim"
app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/pop_exit_anim" />
</fragment>
<fragment <fragment
android:id="@+id/navigation_data_fields" android:id="@+id/navigation_data_fields"
android:name="com.ridgebotics.ridgescout.ui.data.FieldsFragment" android:name="com.ridgebotics.ridgescout.ui.data.FieldsFragment"
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"
app:destination="@id/navigation_data_fields_chooser" app:destination="@id/navigation_data"
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/pop_enter_anim"
app:popExitAnim="@anim/pop_exit_anim" /> app:popExitAnim="@anim/pop_exit_anim" />
</fragment> </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>
@@ -184,21 +129,21 @@
app:destination="@id/navigation_tba" app:destination="@id/navigation_tba"
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/pop_enter_anim"
app:popExitAnim="@anim/pop_exit_anim" /> app:popExitAnim="@anim/pop_exit_anim" />
<action <action
android:id="@+id/action_navigation_transfer_to_navigation_file_selector" android:id="@+id/action_navigation_transfer_to_navigation_file_selector"
app:destination="@id/navigation_file_selector" app:destination="@id/navigation_file_selector"
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/pop_enter_anim"
app:popExitAnim="@anim/pop_exit_anim" /> app:popExitAnim="@anim/pop_exit_anim" />
<action <action
android:id="@+id/action_navigation_transfer_to_navigation_transfer_selector" android:id="@+id/action_navigation_transfer_to_navigation_transfer_selector"
app:destination="@id/navigation_transfer_selector" app:destination="@id/navigation_transfer_selector"
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/pop_enter_anim"
app:popExitAnim="@anim/pop_exit_anim" /> app:popExitAnim="@anim/pop_exit_anim" />
</fragment> </fragment>
@@ -211,7 +156,7 @@
app:destination="@id/navigation_transfer_selector" app:destination="@id/navigation_transfer_selector"
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/pop_enter_anim"
app:popExitAnim="@anim/pop_exit_anim" /> app:popExitAnim="@anim/pop_exit_anim" />
</fragment> </fragment>
@@ -224,28 +169,28 @@
app:destination="@id/navigation_code_generator" app:destination="@id/navigation_code_generator"
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/pop_enter_anim"
app:popExitAnim="@anim/pop_exit_anim" /> app:popExitAnim="@anim/pop_exit_anim" />
<action <action
android:id="@+id/action_navigation_transfer_selector_to_navigation_bluetooth_sender" android:id="@+id/action_navigation_transfer_selector_to_navigation_bluetooth_sender"
app:destination="@id/navigation_bluetooth_sender" app:destination="@id/navigation_bluetooth_sender"
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/pop_enter_anim"
app:popExitAnim="@anim/pop_exit_anim" /> app:popExitAnim="@anim/pop_exit_anim" />
<action <action
android:id="@+id/action_navigation_transfer_selector_to_navigation_code_scanner" android:id="@+id/action_navigation_transfer_selector_to_navigation_code_scanner"
app:destination="@id/navigation_code_scanner" app:destination="@id/navigation_code_scanner"
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/pop_enter_anim"
app:popExitAnim="@anim/pop_exit_anim" /> app:popExitAnim="@anim/pop_exit_anim" />
<action <action
android:id="@+id/action_navigation_transfer_selector_to_navigation_bluetooth_receiver" android:id="@+id/action_navigation_transfer_selector_to_navigation_bluetooth_receiver"
app:destination="@id/navigation_bluetooth_receiver" app:destination="@id/navigation_bluetooth_receiver"
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/pop_enter_anim"
app:popExitAnim="@anim/pop_exit_anim" /> app:popExitAnim="@anim/pop_exit_anim" />
</fragment> </fragment>
+5
View File
@@ -13,4 +13,9 @@
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item> <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. --> <!-- Customize your theme here. -->
</style> </style>
<style name="FullScreenDialogStyle" parent="Theme.MaterialComponents.Dialog">
<item name="android:windowIsFloating">false</item>
<item name="android:windowBackground">@android:color/black</item>
</style>
</resources> </resources>
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<color name="main_200">#FFBBFC86</color> <color name="main_200">#FFA0E044</color>
<color name="main_500">#FF62EE00</color> <color name="main_500">#FF62EE00</color>
<color name="main_700">#FF37B300</color> <color name="main_700">#FF37B300</color>
<color name="teal_200">#FF03DAC5</color> <color name="teal_200">#FF03DAC5</color>
+5
View File
@@ -13,4 +13,9 @@
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item> <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. --> <!-- Customize your theme here. -->
</style> </style>
<style name="FullScreenDialogStyle" parent="Theme.MaterialComponents.Light.Dialog">
<item name="android:windowIsFloating">false</item>
<item name="android:windowBackground">@android:color/white</item>
</style>
</resources> </resources>
+5 -1
View File
@@ -1,5 +1,5 @@
[versions] [versions]
agp = "8.5.1" agp = "8.8.0"
junit = "4.13.2" junit = "4.13.2"
junitVersion = "1.1.5" junitVersion = "1.1.5"
espressoCore = "3.5.1" espressoCore = "3.5.1"
@@ -8,9 +8,11 @@ material = "1.10.0"
constraintlayout = "2.1.4" constraintlayout = "2.1.4"
lifecycleLivedataKtx = "2.6.1" lifecycleLivedataKtx = "2.6.1"
lifecycleViewmodelKtx = "2.6.1" lifecycleViewmodelKtx = "2.6.1"
material3 = "1.3.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" }
@@ -21,9 +23,11 @@ material = { group = "com.google.android.material", name = "material", version.r
constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "lifecycleLivedataKtx" } lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "lifecycleLivedataKtx" }
lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" } lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" }
material3 = { module = "androidx.compose.material3:material3", version.ref = "material3" }
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" }
+1 -1
View File
@@ -1,6 +1,6 @@
#Sun Mar 24 10:48:55 MDT 2024 #Sun Mar 24 10:48:55 MDT 2024
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
+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

Before

Width:  |  Height:  |  Size: 128 KiB

After

Width:  |  Height:  |  Size: 128 KiB

Before

Width:  |  Height:  |  Size: 158 KiB

After

Width:  |  Height:  |  Size: 158 KiB

Before

Width:  |  Height:  |  Size: 162 KiB

After

Width:  |  Height:  |  Size: 162 KiB