86 Commits

Author SHA1 Message Date
Michael Mikovsky 79e8e7cbfc Work on adding better field image, some stuff still needs to be fixed 2025-10-08 11:15:17 -06:00
Michael Mikovsky 311dfcbd5d Add TextViewBuilder.java 2025-09-25 15:38:11 -06:00
Michael Mikovsky fa47eb1aff Add some comments 2025-09-21 12:53:59 -06:00
Michael Mikovsky 93b58310bf Update gradle 2025-09-05 10:55:45 -06:00
Michael Mikovsky 859d3bc773 Make delete file menu. Add #8 2025-08-03 22:28:55 -06:00
Michael Mikovsky 9136df04df Add app version info button 2025-08-02 20:22:47 -06:00
Michael Mikovsky f281ff2f0a Fix #16 2025-08-01 18:16:26 -06:00
Michael Mikovsky 154e76fbf7 Add view team on TBA and Statbotics buttons 2025-07-28 14:00:48 -06:00
Michael Mikovsky ffaec948b4 Proper downloading of images 2025-07-28 13:44:06 -06:00
Michael Mikovsky 2d3db09aae Delete file menu, proper downloading of images 2025-07-28 13:43:42 -06:00
Michael Mikovsky 05a40e39c3 Stuff 2025-07-27 21:57:33 -06:00
Michael Mikovsky 090a0579b9 Settings buttons are now defined programmatically 2025-07-27 19:15:46 -06:00
Michael Mikovsky a30664000c Reformat reciever 2025-07-27 16:28:10 -06:00
Michael Mikovsky 1bae273abd Fix bugs related to qr code scanning. Fixes #13 2025-07-27 16:27:31 -06:00
Michael Mikovsky 5c2b1fef2e Remove asynctask from codescannertask 2025-07-27 12:58:03 -06:00
Michael Mikovsky dbba56e649 Fix code scanning sliders and remove asynctask 2025-07-27 12:57:35 -06:00
Michael Mikovsky 4aa31b620d Settings page improvements 2025-07-25 13:54:36 -06:00
Michael Mikovsky 890b879ef9 Solve random things 2025-07-25 12:51:01 -06:00
Daniel Carta 5279c085e1 Match Scouting Indicator Overhaul
Redo of the UI for the scouting indicator and overall enhancement of the UI
2025-06-17 11:15:36 -06:00
Michael Mikovsky 41460fcd7e Use android log. Fixes #5 2025-05-27 09:29:57 -06:00
Michael Mikovsky 782fb73050 Delete TODO.md 2025-05-26 11:18:01 -06:00
Michael Mikovsky 7e9954d78a Merge pull request #6 from Team4388/python-file-transfer
Add HTTP file transfer
2025-05-26 17:07:25 +00:00
Michael Mikovsky 65baecac35 Fix crash 2025-05-26 10:13:28 -06:00
Michael Mikovsky e278bc10a1 Add java side of http syncing 2025-05-25 18:48:02 -06:00
Michael Mikovsky 5d727cf359 Add upload and download to python server 2025-05-25 13:51:45 -06:00
Michael Mikovsky ae147771cb Start working on the server 2025-05-25 13:44:37 -06:00
Michael Mikovsky 36801e2f9b Add comments to classes and update TODO 2025-04-21 12:06:37 -06:00
Michael Mikovsky 50419e284c Fix sorting crash 2025-04-13 10:45:33 -06:00
Michael Mikovsky c8c278b316 Finish up report tool 2025-04-12 14:22:27 -06:00
Michael Mikovsky e280fc8523 Add scout notice, start work on scouting report system 2025-04-11 22:07:41 -06:00
Michael Mikovsky e559b4dac9 Add mutli-user scouting data attribution 2025-04-10 14:17:46 -06:00
Michael Mikovsky b83d67359e Add scouting dashboard details 2025-04-10 14:00:31 -06:00
Michael Mikovsky 15c503bbb7 Standardize colors 2025-04-10 11:55:06 -06:00
Michael Mikovsky e2b0eee354 Fix candlestick problems 2025-04-10 10:52:58 -06:00
Michael Mikovsky ea41c5db24 Backwards compatibility 2025-04-09 22:07:52 -06:00
Michael Mikovsky 2210d8d654 Add candlestick charts 2025-04-09 18:35:28 -06:00
Michael Mikovsky 9f2ef1b5ed Add field image option 2025-04-08 12:45:33 -06:00
Michael Mikovsky 2dfad418e3 Add revert button 2025-04-08 12:27:09 -06:00
Michael Mikovsky 465b94ac7e Add fields displayed within field editor 2025-04-08 11:48:23 -06:00
Michael Mikovsky 72921a7c11 Fix field updating issue 2025-04-07 21:41:34 -06:00
Michael Mikovsky eaa14adbd9 Work on Field Editor, Improve capitalization of classes 2025-04-04 14:15:30 -06:00
Michael Mikovsky a371f2f449 Add UUIDs, Work on redesigning fields editor 2025-04-03 08:38:24 -06:00
Michael Mikovsky 6c38147c6d Improve TBA event downloading 2025-04-01 12:41:37 -06:00
Michael Mikovsky 3f4a2309b3 Add loading indicator for field data 2025-03-31 16:37:29 -06:00
Michael Mikovsky 5447b64857 Change header byte length, add data view by field type 2025-03-31 11:58:45 -06:00
Michael Mikovsky 967dc3c967 Add rescout option 2025-03-26 10:56:05 -06:00
Michael Mikovsky d2c075da9c Add options for team number and quick alliance swap 2025-03-25 12:06:03 -06:00
Michael Mikovsky fbdd2206c0 Move fields to settings, add setting for match number 2025-03-25 11:50:16 -06:00
Michael Mikovsky 71ef386075 Add team icons and colored cards 2025-03-24 22:29:29 -06:00
Michael Mikovsky ee3c3b6b9c Improve error popup system 2025-03-24 12:41:43 -06:00
Michael Mikovsky 534207ebb1 Fix more scouting data crashing 2025-03-15 06:09:47 -06:00
Michael Mikovsky d458ad7669 Update Version Number 2025-03-07 12:31:10 -07:00
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
177 changed files with 11078 additions and 5578 deletions
+7
View File
@@ -1,6 +1,13 @@
# Python server
__pycache__/
metadata.json
api_key.txt
server_data/
# 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
+11 -15
View File
@@ -1,6 +1,13 @@
![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)
#### Features
- 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,21 +15,10 @@
- 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.
#### Things that are yet to be implemented:
- A page that lets users cross-compare scouting data between teams. (Compare)
- A page that lets scouters more easily make reports to the drive team before a match starts (Report)
- More types of fields
- Data cloud sync using an FTP server
- Deployment on F-Droid - Deployment on F-Droid
- Data cloud sync using an FTP server
#### Things that may or may not be implemented:
- Practice mode
- Statbotics intgration
- Scout error estimation using OPR-like calculation
- - Would most likely require Statbotics
### Screenshots ### 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)|
-38
View File
@@ -1,38 +0,0 @@
### TODO:
##### Scouting:
- Make practice mode??
##### Data Analysis:
- Statbotics intigration???
- Make the "Compare" menu, cross comparing team's stats.
##### Functionality:
- Add more types of data fields.
- Test the scouting app
- Write docs
### In Progress:
##### Scouting:
##### Data Analysis:
- AI overview of scouting data for a team???
- Make the "Report" menu, A tool that lets users select data to display from the the teams and compare menus.
##### Functionality:
- Make server software to allow for easy sync over wifi - FTP
- Deploy to F-Droid
### Done:
##### Scouting:
- Add an "unselect" option to all of the scouting fields
- When a field is created, make updated scouting data return null values, not the default value
- Fix scouting offset bug
##### Data Analysis:
- Add "history" view type to the teams view menu.
- Sentiment analysis of text input type
- Add CSV exporting of match scouting data.
##### Functionality:
- Improve the code scanning progress indicator. It has a rounding error, I think.
- Fix navigation crashes.
- Make everything use Fragments instead of views that toggle visibility
- Make the file browser UI
- Bluetooth data sync
- Formalize error messages & stacktraces
- Make pit and match data field builder UIs. I don't want to have to keep editing a variable
- Make the system for blank and unselected fields better.
+12 -15
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 = 11 // **IMPORTANT** Increment this before releasing on github
versionName = "0.4" versionName = "1.4"// **IMPORTANT** Change this before releasing on github (<Year num since 2024>.<Update Version>)
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }
@@ -47,8 +44,8 @@ android {
buildFeatures { buildFeatures {
viewBinding = true viewBinding = true
} }
aaptOptions { androidResources {
noCompress("tflite"); noCompress += listOf("tflite")
} }
} }
@@ -56,37 +53,37 @@ 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("androidx.navigation:navigation-fragment:2.8.9")
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)
androidTestImplementation(libs.espresso.core) androidTestImplementation(libs.espresso.core)
var camerax_version = "1.3.2" var camerax_version = "1.4.2"
implementation("androidx.camera:camera-core:1.3.2") implementation("androidx.camera:camera-core:${camerax_version}")
implementation("androidx.camera:camera-camera2:1.3.2") implementation("androidx.camera:camera-camera2:${camerax_version}")
implementation("androidx.camera:camera-lifecycle:1.3.2") implementation("androidx.camera:camera-lifecycle:${camerax_version}")
implementation("androidx.camera:camera-view:${camerax_version}") implementation("androidx.camera:camera-view:${camerax_version}")
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" />
@@ -3,16 +3,14 @@ package com.ridgebotics.ridgescout;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.view.MenuItem;
import com.ridgebotics.ridgescout.databinding.ActivityMainBinding; import com.ridgebotics.ridgescout.databinding.ActivityMainBinding;
import com.ridgebotics.ridgescout.scoutingData.fields; import com.ridgebotics.ridgescout.scoutingData.Fields;
import com.ridgebotics.ridgescout.utility.SentimentAnalysis; import com.ridgebotics.ridgescout.utility.SentimentAnalysis;
import com.ridgebotics.ridgescout.utility.AlertManager; import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.fileEditor; import com.ridgebotics.ridgescout.utility.FileEditor;
import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.android.material.bottomnavigation.BottomNavigationView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.navigation.NavController; import androidx.navigation.NavController;
import androidx.navigation.NavOptions; import androidx.navigation.NavOptions;
@@ -21,11 +19,14 @@ import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI; import androidx.navigation.ui.NavigationUI;
import com.ridgebotics.ridgescout.utility.settingsManager; import com.ridgebotics.ridgescout.utility.SettingsManager;
import com.google.android.material.navigation.NavigationBarView;
import java.util.Objects; import java.util.Objects;
import java.util.TimeZone;
// Entrypoint for the scouting app.
// Configures stuff like the nav menu
// Also has some extra functions like a better back stack or a way to suppress the back button.
public class MainActivity extends AppCompatActivity { public class MainActivity extends AppCompatActivity {
@@ -39,18 +40,23 @@ public class MainActivity extends AppCompatActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
// Load shared prefrences
settingsManager.prefs = this.getSharedPreferences( SettingsManager.prefs = this.getSharedPreferences(
"com.ridgebotics.ridgescout", Context.MODE_PRIVATE); "com.ridgebotics.ridgescout", Context.MODE_PRIVATE);
if(!fileEditor.fileExist(fields.matchFieldsFilename)){ // Load default match fields
fields.save(fields.matchFieldsFilename, fields.default_match_fields); if(!FileEditor.fileExist(Fields.matchFieldsFilename)){
Fields.save(Fields.matchFieldsFilename, Fields.default_match_fields);
} }
if(!fileEditor.fileExist(fields.pitsFieldsFilename)){ // Load default pit fields
fields.save(fields.pitsFieldsFilename, fields.default_pit_fields); if(!FileEditor.fileExist(Fields.pitsFieldsFilename)){
Fields.save(Fields.pitsFieldsFilename, Fields.default_pit_fields);
} }
// get time zone for FTP file transfer
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
AlertManager.init(this); AlertManager.init(this);
SentimentAnalysis.init(this); SentimentAnalysis.init(this);
@@ -72,7 +78,7 @@ public class MainActivity extends AppCompatActivity {
appBarConfiguration = new AppBarConfiguration.Builder( appBarConfiguration = new AppBarConfiguration.Builder(
R.id.navigation_scouting, R.id.navigation_scouting,
R.id.navigation_data, R.id.navigation_data_parent,
R.id.navigation_transfer, R.id.navigation_transfer,
R.id.navigation_settings) R.id.navigation_settings)
.build(); .build();
@@ -85,9 +91,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 +101,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 +116,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 +133,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();}
}
} }
@@ -0,0 +1,178 @@
package com.ridgebotics.ridgescout.scoutingData;
import com.ridgebotics.ridgescout.types.input.CheckboxType;
import com.ridgebotics.ridgescout.types.input.DropdownType;
import com.ridgebotics.ridgescout.types.input.FieldposType;
import com.ridgebotics.ridgescout.types.input.FieldType;
import com.ridgebotics.ridgescout.types.input.NumberType;
import com.ridgebotics.ridgescout.types.input.TallyType;
import com.ridgebotics.ridgescout.types.input.TextType;
import com.ridgebotics.ridgescout.types.input.SliderType;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.FileEditor;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder;
import java.util.ArrayList;
import java.util.UUID;
// The mechanism to load, save, and create the fields based off of the raw types from ScoutingDataWriter.java
public class Fields {
// public static ScoutingVersion sv = new ScoutingVersion();
public static final String matchFieldsFilename = "matches.fields";
public static final String pitsFieldsFilename = "pits.fields";
private static String uuid(){
return UUID.randomUUID().toString();
}
public static final FieldType[][] default_match_fields = new FieldType[][] {
{
new FieldposType(uuid(),"Auto start pos", "Where does the robot start its auto?", FieldposType.DEFAULT_FIELD_IMAGE, new int[]{0,0}),
new TallyType(uuid(),"Auto L4 Coral", "How many coral did this robot score in L4 during auto?", 0),
new TallyType(uuid(),"Auto L3 Coral", "How many coral did this robot score in L3 during auto?", 0),
new TallyType(uuid(),"Auto L2 Coral", "How many coral did this robot score in L2 during auto?", 0),
new TallyType(uuid(),"Auto L1/Trough Coral", "How many coral did this robot score in L1 during auto?", 0),
new TallyType(uuid(),"Auto Processor Algae", "How many algae did this robot score in the Barge during auto?", 0),
new TallyType(uuid(),"Auto Barge Algae", "How many algae did this robot score in the Barge during auto?", 0),
new DropdownType(uuid(),"Auto Quality", "How did the robot drive during auto?", new String[]{"Smooth", "Jittery"}, 0),
new TextType(uuid(),"Auto Comments", "Anything interesting about auto", ""),
new TallyType(uuid(),"Teleop L4 Coral", "How many coral did this robot score in L4 during teleop?", 0),
new TallyType(uuid(),"Teleop L3 Coral", "How many coral did this robot score in L3 during teleop?", 0),
new TallyType(uuid(),"Teleop L2 Coral", "How many coral did this robot score in L2 during teleop?", 0),
new TallyType(uuid(),"Teleop L1 Coral", "How many coral did this robot score in L1 during teleop?", 0),
new TallyType(uuid(),"Teleop Processor Algae", "How many algae did this robot score in the Barge during teleop?", 0),
new TallyType(uuid(),"Teleop Barge Algae", "How many algae did this robot score in the Barge during teleop?", 0),
new CheckboxType(uuid(),"Upper Algae Removal", "Did the robot remove upper Algae?", 0),
new CheckboxType(uuid(),"Lower Algae Removal", "Did the robot remove lower Algae?", 0),
new DropdownType(uuid(),"Teleop Quality", "How did the robot drive during Teleop?", new String[]{"Smooth", "Jittery"}, 0),
new TextType(uuid(),"Teleop Comments", "Anything interesting about Teleop", ""),
new DropdownType(uuid(),"Climb State", "What was the final condition of the robot?", new String[]{"Nothing", "Continued Cycling", "Park", "Attempted Shallow", "Shallow", "Attempted Deep", "Deep"}, 0),
new DropdownType(uuid(),"Robot Condition", "Was anything broken?", new String[]{"Everything was working", "Something was maybe broken", "Something was broken", "Robot was disabled for part of the match", "Missing robot"}, 0),
new TextType(uuid(),"Other Comments", "Any other comments you have", "")
}
};
public static final FieldType[][] default_pit_fields = new FieldType[][] {
{
new DropdownType(uuid(),"Drivetrain type", "What type of drivetrain does this team have?", new String[]{"Swerve Drive", "Tank Drive (Differential)", "Other, Info in comments"}, 0),
new DropdownType(uuid(),"Intake type", "What type of intake does this team have?", new String[]{"Ground only", "Player Station only", "Both", "Other, Info in comments"}, 0),
new DropdownType(uuid(),"Intake Consistency", "How consistent is the robot at intakeing?", new String[]{"Does not work", "Worked a few times during testing", "Works most of the time", "Fails sometimes", "Never fails"}, 0),
new DropdownType(uuid(),"Score Area", "What does this robot score?", new String[]{"Only Algae", "Mostly Algae", "Both", "Mostly Coral", "Only Coral"}, 0),
new CheckboxType(uuid(),"L4 Scoring", "Will the robot score in Layer 4?", 0),
new CheckboxType(uuid(),"L3 Scoring", "Will the robot score in Layer 3?", 0),
new CheckboxType(uuid(),"L2 Scoring", "Will the robot score in Layer 3?", 0),
new CheckboxType(uuid(),"L1/Trough Scoring", "Will the robot score in Layer 1?", 0),
new CheckboxType(uuid(),"Processor Scoring", "Will the robot score in the Processor?", 0),
new CheckboxType(uuid(),"Barge Scoring", "Will the robot score algae in the Barge?", 0),
new DropdownType(uuid(),"Scoring Consistency", "How consistent is the robot at Scoring?", new String[]{"Does not work", "Worked a few times during testing", "Works most of the time", "Fails sometimes", "Never fails"}, 0),
new TextType(uuid(),"Auto Capability", "What autos does this team have?", ""),
new DropdownType(uuid(),"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(uuid(),"Climb type", "What does the robot do to climb?", new String[]{"No Climb", "Only Shallow", "Only Deep", "Both Shallow and Deep"}, 0),
new DropdownType(uuid(),"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(uuid(),"Cool Comments", "Is there anything cool about the robot?", ""),
new TextType(uuid(),"Comments", "Things go here", "Day 1:\n\nDay 2:\n\nDay 3:\n")
}
};
public static boolean save(String filename, FieldType[][] values){
try {
ByteBuilder bb = new ByteBuilder();
for (int i = 0; i < values.length; i++) {
bb.addRaw(127, save_version(values[i]));
}
FileEditor.writeFile(filename, bb.build());
return true;
}catch (ByteBuilder.buildingException e) {
AlertManager.error(e);
return false;
// throw new RuntimeException(e);
}
}
private static byte[] save_version(FieldType[] values) throws ByteBuilder.buildingException {
ByteBuilder bb = new ByteBuilder();
for(int i =0; i < values.length; i++){
bb.addRaw(values[i].get_byte_id(), values[i].encode());
}
return bb.build();
}
public static FieldType[][] load(String filename){
byte[] bytes = FileEditor.readFile(filename);
// System.out.println(bytes);
try {
BuiltByteParser bbp = new BuiltByteParser(bytes);
ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse();
FieldType[][] values = new FieldType[objects.size()][];
for(int i = 0 ; i < objects.size(); i++){
values[i] = load_version((byte[]) objects.get(i).get());
}
return values;
} catch (Exception e) {
AlertManager.error(e);
return null;
}
}
private static FieldType[] load_version(byte[] bytes) throws BuiltByteParser.byteParsingExeption{
BuiltByteParser bbp = new BuiltByteParser(bytes);
ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse();
FieldType[] output = new FieldType[objects.size()];
for(int i = 0 ; i < objects.size(); i++){
BuiltByteParser.parsedObject obj = objects.get(i);
FieldType t = null;
switch (obj.getType()){
case FieldType.slider_type_id:
t = new SliderType();
break;
case FieldType.dropdownType:
t = new DropdownType();
break;
case FieldType.notesType:
t = new TextType();
break;
case FieldType.tallyType:
t = new TallyType();
break;
case FieldType.numberType:
t = new NumberType();
break;
case FieldType.checkboxType:
t = new CheckboxType();
break;
case FieldType.fieldposType:
t = new FieldposType();
break;
}
t.decode((byte[]) obj.get());
output[i] = t;
}
return output;
}
}
@@ -1,25 +1,30 @@
package com.ridgebotics.ridgescout.scoutingData; package com.ridgebotics.ridgescout.scoutingData;
import com.ridgebotics.ridgescout.scoutingData.transfer.transferType; import android.util.Log;
import com.ridgebotics.ridgescout.scoutingData.transfer.TransferType;
import com.ridgebotics.ridgescout.types.ScoutingArray; import com.ridgebotics.ridgescout.types.ScoutingArray;
import com.ridgebotics.ridgescout.types.data.dataType; import com.ridgebotics.ridgescout.types.data.RawDataType;
import com.ridgebotics.ridgescout.types.data.intArrType; import com.ridgebotics.ridgescout.types.data.IntArrType;
import com.ridgebotics.ridgescout.types.data.stringType; import com.ridgebotics.ridgescout.types.data.StringType;
import com.ridgebotics.ridgescout.types.input.inputType; import com.ridgebotics.ridgescout.types.input.FieldType;
import com.ridgebotics.ridgescout.types.data.intType; import com.ridgebotics.ridgescout.types.data.IntType;
import com.ridgebotics.ridgescout.utility.AlertManager; import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.fileEditor; import com.ridgebotics.ridgescout.utility.FileEditor;
import com.ridgebotics.ridgescout.utility.BuiltByteParser; import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder; import com.ridgebotics.ridgescout.utility.ByteBuilder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
// Mostly an extension of Fields.java. Saves the raw data from each Field Type.
public class ScoutingDataWriter { public class ScoutingDataWriter {
// private static final int int_type_id = 255; // private static final int int_type_id = 255;
// private static final int string_type_id = 254; // private static final int string_type_id = 254;
public static boolean save(int version, String username, String filename, dataType[] data){ public static boolean save(int version, String username, String filename, RawDataType[] data){
ByteBuilder bb = new ByteBuilder(); ByteBuilder bb = new ByteBuilder();
try { try {
bb.addInt(version); bb.addInt(version);
@@ -28,19 +33,19 @@ public class ScoutingDataWriter {
switch (data[i].getValueType()){ switch (data[i].getValueType()){
case NUM: case NUM:
bb.addInt((int) data[i].forceGetValue()); bb.addInt((int) data[i].forceGetValue());
System.out.println("Saved INT: " + data[i].getName() + ", ("+ data[i].get() +")"); Log.i(ScoutingDataWriter.class.toString(),"Saved INT: " + data[i].getUUID() + ", ("+ data[i].get() +")");
break; break;
case STRING: case STRING:
bb.addString((String) data[i].forceGetValue()); bb.addString((String) data[i].forceGetValue());
System.out.println("Saved STR: " + data[i].getName() + ", ("+ data[i].get() +")"); Log.i(ScoutingDataWriter.class.toString(), "Saved STR: " + data[i].getUUID() + ", ("+ data[i].get() +")");
break; break;
case NUMARR: case NUMARR:
bb.addIntArray((int[]) data[i].forceGetValue()); bb.addIntArray((int[]) data[i].forceGetValue());
System.out.println("Saved INT Array: " + data[i].getName() + ", ("+ Arrays.toString((int[]) data[i].get()) +")"); Log.i(ScoutingDataWriter.class.toString(), "Saved INT Array: " + data[i].getUUID() + ", ("+ Arrays.toString((int[]) data[i].get()) +")");
} }
} }
byte[] bytes = bb.build(); byte[] bytes = bb.build();
fileEditor.writeFile(filename, bytes); FileEditor.writeFile(filename, bytes);
return true; return true;
} catch (ByteBuilder.buildingException e) { } catch (ByteBuilder.buildingException e) {
AlertManager.error(e); AlertManager.error(e);
@@ -55,38 +60,45 @@ public class ScoutingDataWriter {
public ScoutingArray data; public ScoutingArray data;
} }
public static ParsedScoutingDataResult load(String filename, inputType[][] values , transferType[][] transferValues){ public static ParsedScoutingDataResult load(String filename, FieldType[][] values , TransferType[][] transferValues) throws BuiltByteParser.byteParsingExeption{
byte[] bytes = fileEditor.readFile(filename); byte[] bytes = FileEditor.readFile(filename);
BuiltByteParser bbp = new BuiltByteParser(bytes); BuiltByteParser bbp = new BuiltByteParser(bytes);
try {
// try {
ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse(); ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse();
dataType[] dataTypes = new dataType[objects.size()-2]; RawDataType[] rawDataTypes = new RawDataType[objects.size()-2];
int version = ((int)objects.get(0).get()); int version = ((int)objects.get(0).get());
if(values.length <= version) {
// AlertManager.addSimpleError("Error loading " + filename);
throw new BuiltByteParser.byteParsingExeption("Field version (" +version + ") is too recent as compared to latest version (" + (values.length-1) + ")!");
}
// System.out.println(version); // System.out.println(version);
String username = (String) objects.get(1).get(); String username = (String) objects.get(1).get();
for(int i = 0; i < values[version].length; i++){ for(int i = 0; i < values[version].length; i++){
switch (objects.get(i+2).getType()){ switch (objects.get(i+2).getType()){
case 1: // Int case 1: // Int
dataTypes[i] = intType.newNull(values[version][i].name); rawDataTypes[i] = IntType.newNull(values[version][i].UUID);
dataTypes[i].forceSetValue(objects.get(i+2).get()); rawDataTypes[i].forceSetValue(objects.get(i+2).get());
System.out.println("Loaded INT: " + values[version][i].name + ", ("+ dataTypes[i].get() +")"); Log.i(ParsedScoutingDataResult.class.toString(),"Loaded INT: " + values[version][i].name + " (" + values[version][i].UUID + ") " + ", ("+ rawDataTypes[i].get() +")");
break; break;
case 2: // String case 2: // String
dataTypes[i] = stringType.newNull(values[version][i].name); rawDataTypes[i] = StringType.newNull(values[version][i].UUID);
dataTypes[i].forceSetValue(objects.get(i+2).get()); rawDataTypes[i].forceSetValue(objects.get(i+2).get());
System.out.println("Loaded STR: " + values[version][i].name + ", ("+ dataTypes[i].get() +")"); Log.i(ParsedScoutingDataResult.class.toString(),"Loaded STR: " + values[version][i].name + " (" + values[version][i].UUID + ") " + ", ("+ rawDataTypes[i].get() +")");
break; break;
case 3: // Int array case 3: // Int array
dataTypes[i] = intArrType.newNull(values[version][i].name); rawDataTypes[i] = IntArrType.newNull(values[version][i].UUID);
dataTypes[i].forceSetValue(objects.get(i+2).get()); rawDataTypes[i].forceSetValue(objects.get(i+2).get());
System.out.println("Loaded intARR: " + values[version][i].name + ", ("+ Arrays.toString((int[])dataTypes[i].get()) +")"); Log.i(ParsedScoutingDataResult.class.toString(),"Loaded intARR: " + values[version][i].name + " (" + values[version][i].UUID + ") " + ", ("+ Arrays.toString((int[]) rawDataTypes[i].get()) +")");
break; break;
} }
} }
ScoutingArray msa = new ScoutingArray(version, dataTypes, values, transferValues); ScoutingArray msa = new ScoutingArray(version, rawDataTypes, values, transferValues);
msa.update(); msa.update();
ParsedScoutingDataResult psda = new ParsedScoutingDataResult(); ParsedScoutingDataResult psda = new ParsedScoutingDataResult();
@@ -98,10 +110,18 @@ public class ScoutingDataWriter {
return psda; return psda;
} catch (BuiltByteParser.byteParsingExeption e){ // }
AlertManager.error(e);
return null;
} }
// A function that takes in a list of names seperated by commas, and adds a name if it is not included
// This is used for multi-scouter attribution to data.
public static String checkAddName(String prevnames, String name){
List<String> names = new ArrayList<>(List.of(prevnames.split(", ")));
if(!names.contains(name))
names.add(name);
return String.join(", ", names);
} }
} }
@@ -1,158 +0,0 @@
package com.ridgebotics.ridgescout.scoutingData;
import com.ridgebotics.ridgescout.types.input.checkboxType;
import com.ridgebotics.ridgescout.types.input.dropdownType;
import com.ridgebotics.ridgescout.types.input.fieldposType;
import com.ridgebotics.ridgescout.types.input.inputType;
import com.ridgebotics.ridgescout.types.input.numberType;
import com.ridgebotics.ridgescout.types.input.tallyType;
import com.ridgebotics.ridgescout.types.input.textType;
import com.ridgebotics.ridgescout.types.input.sliderType;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.fileEditor;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder;
import java.util.ArrayList;
public class fields {
// public static ScoutingVersion sv = new ScoutingVersion();
public static final String matchFieldsFilename = "matches.fields";
public static final String pitsFieldsFilename = "pits.fields";
public static final inputType[][] default_match_fields = new inputType[][] {
{
new fieldposType("Auto start pos", new int[]{0,0}),
new tallyType("Auto Notes", 0),
new sliderType("Auto Performance", 5, 0, 10),
new textType("Auto Comments", ""),
new tallyType("Teleop Notes", 0),
new sliderType("Teleop Performance", 5, 0, 10),
new textType("Teleop Comments", ""),
new sliderType("Overall Driving Performance", 5, 0, 10),
new textType("Overall Driving Comments", ""),
new sliderType("Score area (AMP <-> Speaker)", 5, 0, 10),
new dropdownType("End Condition", new String[]{"Nothing", "Attempted Climb", "Successful Climbed", "Climbed with multiple robots", "Climbed with trap"}, 0),
new dropdownType("Robot Condition", new String[]{"Everything was working", "Something was maybe broken", "Something was broken", "Robot was disabled for part of the match", "Missing robot (Joe Johnson)"}, 0),
new textType("Other Comments", "")
}
};
public static final inputType[][] default_pit_fields = new inputType[][] {
{
new sliderType("How good is robot", 5, 0, 10),
new sliderType("Test", 1, 0, 10),
new textType("notes", ""),
}
};
public static boolean save(String filename, inputType[][] values){
try {
ByteBuilder bb = new ByteBuilder();
for (int i = 0; i < values.length; i++) {
bb.addRaw(127, save_version(values[i]));
}
fileEditor.writeFile(filename, bb.build());
return true;
}catch (ByteBuilder.buildingException e) {
AlertManager.error(e);
return false;
// throw new RuntimeException(e);
}
}
private static byte[] save_version(inputType[] values) throws ByteBuilder.buildingException {
ByteBuilder bb = new ByteBuilder();
for(int i =0; i < values.length; i++){
bb.addRaw(values[i].get_byte_id(), values[i].encode());
}
return bb.build();
}
public static inputType[][] load(String filename){
byte[] bytes = fileEditor.readFile(filename);
// System.out.println(bytes);
try {
BuiltByteParser bbp = new BuiltByteParser(bytes);
ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse();
inputType[][] values = new inputType[objects.size()][];
for(int i = 0 ; i < objects.size(); i++){
values[i] = load_version((byte[]) objects.get(i).get());
}
return values;
} catch (Exception e) {
AlertManager.error(e);
return null;
}
}
private static inputType[] load_version(byte[] bytes) throws BuiltByteParser.byteParsingExeption{
BuiltByteParser bbp = new BuiltByteParser(bytes);
ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse();
inputType[] output = new inputType[objects.size()];
for(int i = 0 ; i < objects.size(); i++){
BuiltByteParser.parsedObject obj = objects.get(i);
inputType t = null;
switch (obj.getType()){
case inputType.slider_type_id:
t = new sliderType();
break;
case inputType.dropdownType:
t = new dropdownType();
break;
case inputType.notesType:
t = new textType();
break;
case inputType.tallyType:
t = new tallyType();
break;
case inputType.numberType:
t = new numberType();
break;
case inputType.checkboxType:
t = new checkboxType();
break;
case inputType.fieldposType:
t = new fieldposType();
break;
}
t.decode((byte[]) obj.get());
output[i] = t;
}
return output;
}
// public static void test(){
// ScoutingVersion.transferType[][] transferValues = sv.get_transfer_values(values);
//
// ScoutingVersion.ScoutingArray msa = sv.new ScoutingArray(0, new ScoutingVersion.dataType[]{
// sv.new stringType("name", "test-username"),
// sv.new intType("How good is robot", 12)
// }, values, transferValues);
//
// msa.update();
//
// for(ScoutingVersion.dataType dt : msa.array){
// if(dt == null) continue;
// switch (dt.getValueType()){
// case NUM:
// System.out.println(dt.name + " " + (int) dt.get());
// break;
// case STRING:
// System.out.println(dt.name + " " + (String) dt.get());
// break;
// }
//
// }
// }
}
@@ -0,0 +1,9 @@
package com.ridgebotics.ridgescout.scoutingData.transfer;
// Transfer type if a field was created
public class CreateTransferType extends TransferType {
public transferValue getType() {return transferValue.CREATE;}
public CreateTransferType(String UUID){
super(UUID);
}
}
@@ -0,0 +1,9 @@
package com.ridgebotics.ridgescout.scoutingData.transfer;
// Transfer type if a field directly transfers
public class DirectTransferType extends TransferType {
public transferValue getType() {return transferValue.DIRECT;}
public DirectTransferType(String UUID){
super(UUID);
}
}
@@ -0,0 +1,48 @@
package com.ridgebotics.ridgescout.scoutingData.transfer;
import com.ridgebotics.ridgescout.types.input.FieldType;
// The "Transfer Types" system, a component in the loading of scouting data.
// Dictates how the
// For example, a field can be created in one version, and how the app updates it is by obtaining it's transfer value, in this case being "create".
public abstract class TransferType {
public enum transferValue {
DIRECT,
CREATE
}
public String UUID;
public abstract transferValue getType();
public TransferType(String UUID){
this.UUID = UUID;
}
private static FieldType get_input_type_by_UUID(FieldType[] values, String UUID){
for(FieldType it : values){
if(it.UUID.equals(UUID)){
return it;
}
}
return null;
}
//Inputs:
public static TransferType[][] get_transfer_values(FieldType[][] values) {
TransferType[][] output = new TransferType[values.length][];
for(int a = 1; a < values.length; a++){
TransferType[] v = new TransferType[values[a].length];
for(int b = 0; b < values[a].length; b++){
String UUID = values[a][b].UUID;
if(get_input_type_by_UUID(values[a-1], UUID) != null){
v[b] = new DirectTransferType(UUID);
}else{
v[b] = new CreateTransferType(UUID);
}
}
output[a-1] = v;
}
return output;
}
}
@@ -1,8 +0,0 @@
package com.ridgebotics.ridgescout.scoutingData.transfer;
public class createTransferType extends transferType {
public transferValue getType() {return transferValue.CREATE;}
public createTransferType(String name){
super(name);
}
}
@@ -1,8 +0,0 @@
package com.ridgebotics.ridgescout.scoutingData.transfer;
public class directTransferType extends transferType {
public transferValue getType() {return transferValue.DIRECT;}
public directTransferType(String name){
super(name);
}
}
@@ -1,41 +0,0 @@
package com.ridgebotics.ridgescout.scoutingData.transfer;
import com.ridgebotics.ridgescout.types.input.inputType;
public abstract class transferType {
public enum transferValue {
DIRECT,
CREATE
}
public String name;
public abstract transferValue getType();
public transferType(String name){
this.name = name;
}
private static inputType get_input_type_by_name(inputType[] values, String name){
for(inputType it : values){
if(it.name.equals(name)){
return it;
}
}
return null;
}
public static transferType[][] get_transfer_values(inputType[][] values) {
transferType[][] output = new transferType[values.length][];
for(int a = 1; a < values.length; a++){
transferType[] v = new transferType[values[a].length];
for(int b = 0; b < values[a].length; b++){
String name = values[a][b].name;
if(get_input_type_by_name(values[a-1], name) != null){
v[b] = new directTransferType(name);
}else{
v[b] = new createTransferType(name);
}
}
output[a-1] = v;
}
return output;
}
}
@@ -0,0 +1,179 @@
package com.ridgebotics.ridgescout.types;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder;
import com.ridgebotics.ridgescout.utility.FileEditor;
import java.io.File;
import java.time.Instant;
import java.time.temporal.TemporalField;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.function.Predicate;
public class ColabArray {
private enum Action {
ADD,
REMOVE
}
private List<Diff> changelog = new ArrayList<>();
private void addChange(Diff change) {
this.changelog.add(change);
}
private List<Diff> getChangelog() {
return changelog;
}
public void add(String item) {
Diff diff = new Diff();
diff.action = Action.ADD;
diff.content = item;
diff.time = new Date();
addChange(diff);
}
public void remove(String item) {
Diff diff = new Diff();
diff.action = Action.REMOVE;
diff.content = item;
diff.time = new Date();
addChange(diff);
}
public void remove(int index) {
remove(get().get(index));
}
private static class Diff {
public Action action;
public String content;
public Date time;
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj.getClass() != this.getClass()) {
return false;
}
Diff other = (Diff) obj;
return other.action == this.action &&
other.time.getTime() == this.time.getTime() &&
other.content.equals(this.content);
}
}
public byte[] encode() throws ByteBuilder.buildingException{
ByteBuilder bb = new ByteBuilder();
for(Diff change : this.changelog){
bb.addInt(change.action.ordinal());
bb.addString(change.content);
bb.addLong(change.time.getTime());
}
return bb.build();
}
public static ColabArray decode(byte[] bytes) throws BuiltByteParser.byteParsingExeption {
BuiltByteParser bbp = new BuiltByteParser(bytes);
List<BuiltByteParser.parsedObject> results = bbp.parse();
if(results.size() % 3 != 0){
throw new BuiltByteParser.byteParsingExeption("Wrong amount of elements in ColabArray!");
}
ColabArray arr = new ColabArray();
for(int i = 0; i < results.size(); i += 3) {
Diff diff = new Diff();
diff.action = Action.values()[(int) results.get(i).get()];
diff.content = (String) results.get(i+1).get();
diff.time = new Date((long) results.get(i+2).get());
arr.addChange(diff);
}
return arr;
}
public void append(ColabArray other) {
List<Diff> otherlog = other.getChangelog();
otherlog.removeIf(diff ->
this.changelog.contains(diff)
);
this.changelog.addAll(otherlog);
this.changelog = Arrays.asList(sort(this.changelog));
}
public void append(File other) {
byte[] bytes = FileEditor.readFile(other);
if(bytes == null) return;
try {
append(decode(bytes));
} catch (BuiltByteParser.byteParsingExeption e) {
AlertManager.error("Failed to append ColabArray!", e);
}
}
private static Diff[] sort(List<Diff> changelog) {
Diff[] sorted = changelog.toArray(new Diff[0]);
try {
Arrays.sort(sorted, (o1, o2) -> (int) (o1.time.getTime() - o2.time.getTime()));
} catch (Exception e){
AlertManager.error(e);
}
return sorted;
}
public List<String> get() {
List<String> result = new ArrayList<>();
for(Diff change : changelog) {
switch (change.action) {
case ADD:
result.add(change.content);
break;
case REMOVE:
result.remove(change.content);
break;
}
}
return result;
}
public boolean contains(String item) {
// Diff[] sorted = sort();
for(int i = changelog.size()-1; i >= 0; i--) {
Diff change = changelog.get(i);
if(!change.content.equals(item)) continue;
return change.action == Action.ADD;
}
return false;
}
public int size() {
return get().size();
}
}
@@ -1,21 +1,24 @@
package com.ridgebotics.ridgescout.types; package com.ridgebotics.ridgescout.types;
import com.ridgebotics.ridgescout.scoutingData.transfer.createTransferType; import android.util.Log;
import com.ridgebotics.ridgescout.scoutingData.transfer.directTransferType;
import com.ridgebotics.ridgescout.scoutingData.transfer.transferType;
import com.ridgebotics.ridgescout.types.data.dataType;
import com.ridgebotics.ridgescout.types.data.intType;
import com.ridgebotics.ridgescout.types.data.stringType;
import com.ridgebotics.ridgescout.types.input.inputType;
import com.ridgebotics.ridgescout.scoutingData.transfer.CreateTransferType;
import com.ridgebotics.ridgescout.scoutingData.transfer.DirectTransferType;
import com.ridgebotics.ridgescout.scoutingData.transfer.TransferType;
import com.ridgebotics.ridgescout.types.data.RawDataType;
import com.ridgebotics.ridgescout.types.data.IntType;
import com.ridgebotics.ridgescout.types.data.StringType;
import com.ridgebotics.ridgescout.types.input.FieldType;
// A wrapper class for the stuff going on in Fields.java and ScoutingDataWriter.java.
public class ScoutingArray { public class ScoutingArray {
public int version; public int version;
public dataType[] array; public RawDataType[] array;
public inputType[][] values; public FieldType[][] values;
public int latest_version_num; public int latest_version_num;
public transferType[][] transfer_values; public TransferType[][] transfer_values;
public ScoutingArray(int version, dataType[] array, inputType[][] values, transferType[][] transfer_values){ public ScoutingArray(int version, RawDataType[] array, FieldType[][] values, TransferType[][] transfer_values){
this.version = version; this.version = version;
this.array = array; this.array = array;
this.values = values; this.values = values;
@@ -23,53 +26,50 @@ public class ScoutingArray {
this.transfer_values = transfer_values; this.transfer_values = transfer_values;
} }
public ScoutingArray(int version, dataType[] array, inputType[][] values){ public ScoutingArray(int version, RawDataType[] array, FieldType[][] values){
this(version, array, values, transferType.get_transfer_values(values)); this(version, array, values, TransferType.get_transfer_values(values));
} }
public void update(){ public void update(){
while(version<latest_version_num){ while(version<latest_version_num){
dataType[] new_values = new dataType[transfer_values[version].length]; RawDataType[] new_values = new RawDataType[transfer_values[version].length];
for(int i = 0; i < transfer_values[version].length; i++){ for(int i = 0; i < transfer_values[version].length; i++){
transferType tv = transfer_values[version][i]; TransferType tv = transfer_values[version][i];
switch (tv.getType()){ switch (tv.getType()){
case DIRECT: case DIRECT:
new_values[i] = direct_transfer((directTransferType) tv); new_values[i] = direct_transfer((DirectTransferType) tv);
continue; continue;
// case RENAME:
// new_values[i] = rename_transfer((renameTransferType) tv);
// continue;
case CREATE: case CREATE:
new_values[i] = create_transfer((createTransferType) tv); new_values[i] = create_transfer((CreateTransferType) tv);
continue; continue;
} }
} }
this.array = new_values; this.array = new_values;
version++; version++;
System.out.println("Updated to " + version); Log.i(getClass().toString(),"Updated to " + version);
} }
} }
private inputType get_input_type_by_name(int version, String name){ private FieldType get_input_type_by_UUID(int version, String UUID){
for(inputType it : values[version]){ for(FieldType it : values[version]){
if(it.name.equals(name)){ if(it.UUID.equals(UUID)){
return it; return it;
} }
} }
return null; return null;
} }
private dataType get_data_type_by_name(String name){ private RawDataType get_data_type_by_UUID(String UUID){
for(dataType dt : array){ for(RawDataType dt : array){
if(dt.getName().equals(name)){ if(dt.getUUID().equals(UUID)){
return dt; return dt;
} }
} }
return null; return null;
} }
private dataType direct_transfer(directTransferType tv){ private RawDataType direct_transfer(DirectTransferType tv){
return get_data_type_by_name(tv.name); return get_data_type_by_UUID(tv.UUID);
} }
// private dataType rename_transfer(renameTransferType tv){ // private dataType rename_transfer(renameTransferType tv){
@@ -78,13 +78,13 @@ public class ScoutingArray {
// return dt; // return dt;
// } // }
private dataType create_transfer(createTransferType tv){ private RawDataType create_transfer(CreateTransferType tv){
inputType it = get_input_type_by_name(version+1, tv.name); FieldType it = get_input_type_by_UUID(version+1, tv.UUID);
switch (it.getValueType()){ switch (it.getValueType()){
case NUM: case NUM:
return intType.newNull(it.name); return IntType.newNull(it.UUID);
case STRING: case STRING:
return stringType.newNull(it.name); return StringType.newNull(it.UUID);
} }
return null; return null;
} }
@@ -3,24 +3,26 @@ package com.ridgebotics.ridgescout.types;
import com.ridgebotics.ridgescout.utility.AlertManager; import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.BuiltByteParser; import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder; import com.ridgebotics.ridgescout.utility.ByteBuilder;
import com.ridgebotics.ridgescout.utility.fileEditor; import com.ridgebotics.ridgescout.utility.FileEditor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects; import java.util.Objects;
public class file { // Basically just holds a byte array and a filename, and then us just encoded.
public class ScoutingFile {
public static final int typecode = 255; public static final int typecode = 255;
public String filename; public String filename;
public byte[] data; public byte[] data;
public file(){} public ScoutingFile(){}
public file(String filename){ public ScoutingFile(String filename){
this(filename, fileEditor.readFile(filename)); this(filename, FileEditor.readFile(filename));
} }
public file(String filename, byte[] data){ public ScoutingFile(String filename, byte[] data){
this.filename = filename; this.filename = filename;
this.data = data; this.data = data;
} }
@@ -28,8 +30,15 @@ public class file {
public byte[] encode(){ public byte[] encode(){
try { try {
ByteBuilder bb = new ByteBuilder() ByteBuilder bb = new ByteBuilder()
.addString(filename) .addString(filename);
.addRaw(255, Objects.requireNonNull(fileEditor.readFile(filename)));
// byte[] data = Objects.requireNonNull(fileEditor.readFile(filename));
// for(int i = 0; i < data.length / 65535; i++){
// bb.addRaw(255, fileEditor.getByteBlock(data, i*65535, (i+1)*65535));
// }
bb.addRaw(255, Objects.requireNonNull(FileEditor.readFile(filename)));
return bb.build(); return bb.build();
@@ -39,18 +48,18 @@ public class file {
} }
} }
public static file decode(byte[] bytes){ public static ScoutingFile decode(byte[] bytes){
try{ try{
ArrayList<BuiltByteParser.parsedObject> objects = new BuiltByteParser(bytes).parse(); ArrayList<BuiltByteParser.parsedObject> objects = new BuiltByteParser(bytes).parse();
file f = new file(); ScoutingFile f = new ScoutingFile();
f.filename = (String) objects.get(0).get(); f.filename = (String) objects.get(0).get();
f.data = (byte[]) objects.get(1).get(); f.data = (byte[]) objects.get(1).get();
return f; return f;
}catch (BuiltByteParser.byteParsingExeption e){ }catch (Exception e){
AlertManager.error(e); AlertManager.error(e);
return null; return null;
} }
@@ -58,6 +67,6 @@ public class file {
public boolean write(){ public boolean write(){
if(data == null || filename == null) return false; if(data == null || filename == null) return false;
return fileEditor.writeFile(filename, data); return FileEditor.writeFile(filename, data);
} }
} }
@@ -1,6 +1,8 @@
package com.ridgebotics.ridgescout.types.data; package com.ridgebotics.ridgescout.types.data;
public class intArrType extends dataType {
// Int array raw data type
public class IntArrType extends RawDataType {
public static final int[] nullval = new int[]{255, 255}; public static final int[] nullval = new int[]{255, 255};
// public static final int unselectedval = 1; // public static final int unselectedval = 1;
@@ -23,13 +25,13 @@ public class intArrType extends dataType {
forceSetValue((int[]) value); forceSetValue((int[]) value);
} }
public intArrType(String name, int[] value) { public IntArrType(String name, int[] value) {
super(name); super(name);
set(value); set(value);
} }
public static intArrType newNull(String name){ public static IntArrType newNull(String name){
return new intArrType(name, nullval); return new IntArrType(name, nullval);
} }
// public static intType newUnselected(String name){ // public static intType newUnselected(String name){
@@ -1,6 +1,7 @@
package com.ridgebotics.ridgescout.types.data; package com.ridgebotics.ridgescout.types.data;
public class intType extends dataType { // Number raw data type
public class IntType extends RawDataType {
public static final int nullval = 255; public static final int nullval = 255;
// public static final int unselectedval = 1; // public static final int unselectedval = 1;
@@ -23,13 +24,13 @@ public class intType extends dataType {
forceSetValue((int) value); forceSetValue((int) value);
} }
public intType(String name, int value) { public IntType(String name, int value) {
super(name); super(name);
set(value); set(value);
} }
public static intType newNull(String name){ public static IntType newNull(String name){
final intType a = new intType(name, 0); final IntType a = new IntType(name, 0);
a.forceSetValue(nullval); a.forceSetValue(nullval);
return a; return a;
} }
@@ -1,6 +1,7 @@
package com.ridgebotics.ridgescout.types.data; package com.ridgebotics.ridgescout.types.data;
public abstract class dataType { // Abstract class for raw data types for use in fields.
public abstract class RawDataType {
public enum valueTypes { public enum valueTypes {
NUM, NUM,
NUMARR, NUMARR,
@@ -8,7 +9,7 @@ public abstract class dataType {
} }
private Object value; private Object value;
private final String name; private final String UUID;
public abstract valueTypes getValueType(); public abstract valueTypes getValueType();
@@ -24,9 +25,9 @@ public abstract class dataType {
public abstract boolean isNull(); public abstract boolean isNull();
// public abstract boolean isUnselected(); // public abstract boolean isUnselected();
public String getName() {return name;} public String getUUID() {return UUID;}
public dataType(String name){ public RawDataType(String UUID){
this.name = name; this.UUID = UUID;
} }
} }
@@ -1,6 +1,7 @@
package com.ridgebotics.ridgescout.types.data; package com.ridgebotics.ridgescout.types.data;
public class stringType extends dataType{ // String raw data type
public class StringType extends RawDataType {
public static final String nullval = "null"; public static final String nullval = "null";
// public static final String unselectedval = ""; // public static final String unselectedval = "";
@@ -23,13 +24,13 @@ public class stringType extends dataType{
forceSetValue(value); forceSetValue(value);
} }
public stringType(String name, String value) { public StringType(String name, String value) {
super(name); super(name);
forceSetValue(value); forceSetValue(value);
} }
public static stringType newNull(String name){ public static StringType newNull(String name){
final stringType a = new stringType(name, ""); final StringType a = new StringType(name, "");
a.forceSetValue(nullval); a.forceSetValue(nullval);
return a; return a;
} }
@@ -2,20 +2,20 @@ package com.ridgebotics.ridgescout.types;
import static com.ridgebotics.ridgescout.utility.DataManager.event; import static com.ridgebotics.ridgescout.utility.DataManager.event;
import android.widget.TableRow;
import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import com.ridgebotics.ridgescout.utility.AlertManager; import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.BuiltByteParser; import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder; import com.ridgebotics.ridgescout.utility.ByteBuilder;
import com.ridgebotics.ridgescout.utility.SettingsManager;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.stream.IntStream; import java.util.stream.IntStream;
// Class to contain data for an entire event.
// Easily encoded and decoded to binary format.
public class frcEvent { public class frcEvent {
public static final int typecode = 254;
public String eventCode; public String eventCode;
public String name; public String name;
public ArrayList<frcMatch> matches; public ArrayList<frcMatch> matches;
@@ -35,6 +35,10 @@ public class frcEvent {
bb.addRaw(frcMatch.typecode, match.encode()); bb.addRaw(frcMatch.typecode, match.encode());
} }
if(SettingsManager.getEVCode().equals("unset")){
SettingsManager.setEVCode(eventCode);
}
return bb.build(); return bb.build();
} catch (ByteBuilder.buildingException e) { } catch (ByteBuilder.buildingException e) {
AlertManager.error(e); AlertManager.error(e);
@@ -117,28 +121,85 @@ public class frcEvent {
return maxMatch; return maxMatch;
} }
// public public frcMatch getNextTeamMatch(int teamNum, int curMatch){
frcMatch[] teamMatches = getTeamMatches(teamNum);
// Returns the soonest match that there will be all the possible upcoming data on other teams
public void getReportMatches(int ourTeamNum){
frcMatch[] teamMatches = event.getTeamMatches(ourTeamNum);
for(int i = 0; i < teamMatches.length; i++) { for(int i = 0; i < teamMatches.length; i++) {
int maxMatch = -1; if (teamMatches[i].matchIndex > curMatch)
return teamMatches[i];
}
return null;
}
// Returns the soonest match that there will be all the possible upcoming data on other teams
public int getMostInformedBy(int ourTeamNum, int curMatch){
frcMatch teamMatch = getNextTeamMatch(ourTeamNum, curMatch);
int maxMatch = Integer.MIN_VALUE;
for(int a = 0; a < 6; a++){ for(int a = 0; a < 6; a++){
int teamNum; int teamNum;
if(a < 3) if(a < 3)
teamNum = teamMatches[i].redAlliance[a]; teamNum = teamMatch.redAlliance[a];
else else
teamNum = teamMatches[i].blueAlliance[a-3]; teamNum = teamMatch.blueAlliance[a-3];
if(teamNum == ourTeamNum) if(teamNum == ourTeamNum)
continue; continue;
int matchNum = event.getMostRecentTeamMatch(teamNum, teamMatches[i].matchIndex); int matchNum = event.getMostRecentTeamMatch(teamNum, teamMatch.matchIndex);
if(maxMatch < matchNum) if(maxMatch < matchNum)
maxMatch = matchNum; maxMatch = matchNum;
} }
}
return maxMatch;
}
public frcTeam getTeamByNum(int teamNum){
for(int i = 0; i < teams.size(); i++){
frcTeam team = teams.get(i);
if(team.teamNumber == teamNum)
return team;
}
return null;
}
public List<frcTeam> getTeamsSorted() {
int[] teamNums = new int[teams.size()];
for(int i = 0 ; i < teams.size(); i++){
teamNums[i] = teams.get(i).teamNumber;
}
Arrays.sort(teamNums);
List<frcTeam> list = new ArrayList<>();
for(int i = 0; i < teams.size(); i++){
frcTeam team = getTeamByNum(teamNums[i]);
assert team != null;
list.add(team);
}
return list;
}
public boolean getIsBlueAlliance(int teamNum, int matchNum){
return getIsBlueAlliance(teamNum, matches.get(matchNum));
}
public boolean getIsBlueAlliance(int teamNum, frcMatch match){
for(int i = 0; i < match.redAlliance.length; i++)
if(match.redAlliance[i] == teamNum) return false;
for(int i = 0; i < match.blueAlliance.length; i++)
if(match.blueAlliance[i] == teamNum) return true;
return false;
} }
} }
@@ -9,6 +9,8 @@ import com.ridgebotics.ridgescout.utility.ByteBuilder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
// Class to contain data for the six teams numbers in a single match
// Easily encoded and decoded to binary format.
public class frcMatch { public class frcMatch {
public static final int typecode = 253; public static final int typecode = 253;
public frcMatch(){} public frcMatch(){}
@@ -27,7 +29,7 @@ public class frcMatch {
.addInt(redAlliance[1]) .addInt(redAlliance[1])
.addInt(redAlliance[2]) .addInt(redAlliance[2])
.build(); .build();
} catch (ByteBuilder.buildingException e) { } catch (Exception e) {
AlertManager.error(e); AlertManager.error(e);
return new byte[1]; return new byte[1];
} }
@@ -49,7 +51,7 @@ public class frcMatch {
return frc; return frc;
} catch (BuiltByteParser.byteParsingExeption e) { } catch (Exception e) {
AlertManager.error(e); AlertManager.error(e);
return null; return null;
} }
@@ -1,21 +1,36 @@
package com.ridgebotics.ridgescout.types; package com.ridgebotics.ridgescout.types;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import com.ridgebotics.ridgescout.utility.AlertManager; import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.BuiltByteParser; import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder; import com.ridgebotics.ridgescout.utility.ByteBuilder;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
// Class to contain data for a team, in an event.
// Easily encoded and decoded to binary format.
public class frcTeam { public class frcTeam {
public static final int typecode = 252; public static final int typecode = 252;
private static final int[] DEFAULT_COLOR_ARR = new int[]{64,64,64};
private static final int DEFAULT_COLOR = Color.argb(255, DEFAULT_COLOR_ARR[0],DEFAULT_COLOR_ARR[1],DEFAULT_COLOR_ARR[2]);
public int teamNumber = 0; public int teamNumber = 0;
public String teamName = "null"; public String teamName = "null";
public String city = "null"; public String city = "null";
public String stateOrProv = "null"; public String stateOrProv = "null";
public String school = "null"; public String school = "null";
public String country = "null"; public String country = "null";
public Bitmap bitmap = null;
public int[] teamColor = DEFAULT_COLOR_ARR;
public int startingYear = 0; public int startingYear = 0;
public String getDescription(){ public String getDescription(){
@@ -32,6 +47,8 @@ public class frcTeam {
.addString(school) .addString(school)
.addString(country) .addString(country)
.addInt(startingYear) .addInt(startingYear)
.addRaw(127, encodeBitmap(bitmap))
.addIntArray(teamColor)
.build(); .build();
} catch (ByteBuilder.buildingException e) { } catch (ByteBuilder.buildingException e) {
AlertManager.error(e); AlertManager.error(e);
@@ -52,6 +69,13 @@ public class frcTeam {
frc.country = (String) objects.get(5).get(); frc.country = (String) objects.get(5).get();
frc.startingYear = (int) objects.get(6).get(); frc.startingYear = (int) objects.get(6).get();
if(objects.size() == 9){
frc.bitmap = decodeBitmap((BuiltByteParser.rawObject) objects.get(7));
frc.teamColor = (int[]) objects.get(8).get();
// System.out.println(Arrays.toString(frc.teamColor));
}
return frc; return frc;
} catch (BuiltByteParser.byteParsingExeption e) { } catch (BuiltByteParser.byteParsingExeption e) {
@@ -60,6 +84,111 @@ public class frcTeam {
} }
} }
public static byte[] encodeBitmap(Bitmap bitmap){
if(bitmap == null) return new byte[]{0};
ByteArrayOutputStream blob = new ByteArrayOutputStream();
if(bitmap.compress(Bitmap.CompressFormat.PNG, 0 /* Ignored for PNGs */, blob)){
return blob.toByteArray();
}else{
return new byte[]{0};
}
}
public static Bitmap decodeBitmap(BuiltByteParser.rawObject rawObject){
if(rawObject.getType() != 127) return null;
byte[] bytes = (byte[]) rawObject.get();
if(bytes.length <= 1) return null;
return BitmapFactory.decodeByteArray(bytes,0,bytes.length);
}
public static int[] findPrimaryColor(Bitmap bitmap) {
if (bitmap == null) {
return DEFAULT_COLOR_ARR;
}
// Step 1: Posterize the image (reduce color levels)
Bitmap posterizedBitmap = posterize(bitmap, 8); // 8 levels of posterization
// Step 2: Find the most saturated and frequent color
return findMostSaturatedAndFrequentColor(posterizedBitmap);
}
// Posterize the image by reducing color levels
private static Bitmap posterize(Bitmap original, int levels) {
int width = original.getWidth();
int height = original.getHeight();
Bitmap posterized = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
int step = 255 / (levels - 1);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int pixel = original.getPixel(x, y);
int r = Color.red(pixel);
int g = Color.green(pixel);
int b = Color.blue(pixel);
// Quantize the RGB values
r = (r / step) * step;
g = (g / step) * step;
b = (b / step) * step;
int newPixel = Color.rgb(r, g, b);
posterized.setPixel(x, y, newPixel);
}
}
return posterized;
}
// Find the most saturated and frequent color
private static int[] findMostSaturatedAndFrequentColor(Bitmap bitmap) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
Map<Integer, Integer> colorFrequency = new HashMap<>();
// Count the frequency of each color
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int pixel = bitmap.getPixel(x, y);
colorFrequency.put(pixel, colorFrequency.getOrDefault(pixel, 0) + 1);
}
}
// Find the most saturated and frequent color
int primaryColor = DEFAULT_COLOR; // Default fallback
double maxColorness = -1;
int maxFrequency = 0;
for (Map.Entry<Integer, Integer> entry : colorFrequency.entrySet()) {
int color = entry.getKey();
int frequency = entry.getValue();
float[] hsv = new float[3];
Color.colorToHSV(color, hsv);
double colorness = Math.pow(hsv[1],2)+Math.pow(hsv[2],2);
// Prioritize saturation, then frequency
if ((colorness > maxColorness && frequency > maxFrequency * 0.5) && Color.alpha(color) > 127) { //|| (colorness == maxColorness)
maxColorness = colorness;
// maxFrequency = frequency;
primaryColor = color;
}
}
float[] hsv = new float[3];
Color.colorToHSV(primaryColor, hsv);
primaryColor = Color.HSVToColor(new float[]{hsv[0], Math.max(hsv[1], (float) 0.2), Math.max(hsv[2], (float) 0.3)});
return new int[]{Color.red(primaryColor),Color.blue(primaryColor),Color.green(primaryColor)};
}
public int getTeamColor(){
return Color.argb(255,teamColor[0],teamColor[2],teamColor[1]);
}
@NonNull @NonNull
public String toString(){ public String toString(){
return "frcTeam Num: " + teamNumber + ", " + getDescription(); return "frcTeam Num: " + teamNumber + ", " + getDescription();
@@ -1,19 +1,18 @@
package com.ridgebotics.ridgescout.types.input; package com.ridgebotics.ridgescout.types.input;
import static com.ridgebotics.ridgescout.utility.Colors.chart_background;
import static com.ridgebotics.ridgescout.utility.Colors.checkbox_colors;
import static com.ridgebotics.ridgescout.utility.Colors.checkbox_data_color;
import static com.ridgebotics.ridgescout.utility.Colors.checkbox_value_text_color;
import android.content.Context; import android.content.Context;
import android.graphics.Color; import android.graphics.Color;
import android.view.Gravity;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.RelativeLayout; import android.widget.TableLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.charts.PieChart; import com.github.mikephil.charting.charts.PieChart;
@@ -24,69 +23,55 @@ 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.ridgebotics.ridgescout.types.data.dataType; import com.ridgebotics.ridgescout.types.data.RawDataType;
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;
import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
public class checkboxType extends inputType { public class CheckboxType extends FieldType {
public int get_byte_id() {return checkboxType;} public int get_byte_id() {return checkboxType;}
public inputTypes getInputType(){return inputTypes.CHECKBOX;} public inputTypes getInputType(){return inputTypes.CHECKBOX;}
public dataType.valueTypes getValueType(){return dataType.valueTypes.NUM;} public RawDataType.valueTypes getValueType(){return RawDataType.valueTypes.NUM;}
public Object get_fallback_value(){return 0;} public Object get_fallback_value(){return 0;}
public 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 UUID, String name, String description, int isChecked){
super(name); super(UUID, name, description);
this.default_value = isChecked; this.default_value = isChecked;
} }
public byte[] encode() throws ByteBuilder.buildingException { public void encodeData(ByteBuilder bb) throws ByteBuilder.buildingException {
ByteBuilder bb = new ByteBuilder();
bb.addString(name);
bb.addInt((int)default_value); bb.addInt((int)default_value);
return bb.build();
} }
public void decode(byte[] bytes) throws BuiltByteParser.byteParsingExeption {
BuiltByteParser bbp = new BuiltByteParser(bytes);
ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse();
name = (String) objects.get(0).get(); public void decodeData(ArrayList<BuiltByteParser.parsedObject> objects) {
default_value = objects.get(1).get(); default_value = objects.get(0).get();
} }
// public PowerSpinnerView dropdown = null; // public PowerSpinnerView dropdown = null;
public CheckBox checkBox = null; public CheckBox checkBox = null;
public View createView(Context context, Function<dataType, Integer> onUpdate){ public View createView(Context context, Function<RawDataType, 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;
} }
public void setViewValue(Object value) { public void setViewValue(Object value) {
if(checkBox == null) return; if(checkBox == null) return;
if(intType.isNull((int) value)){ if(IntType.isNull((int) value)){
nullify(); nullify();
return; return;
} }
@@ -100,10 +85,10 @@ public class checkboxType extends inputType {
isBlank = true; isBlank = true;
checkBox.setVisibility(View.GONE); checkBox.setVisibility(View.GONE);
} }
public dataType getViewValue(){ public RawDataType getViewValue(){
if(checkBox == null) return null; if(checkBox == null) return null;
if(checkBox.getVisibility() == View.GONE) return new intType(name, intType.nullval); if(checkBox.getVisibility() == View.GONE) return new IntType(name, IntType.nullval);
return new intType(name, checkBox.isChecked() ? 1 : 0); return new IntType(name, checkBox.isChecked() ? 1 : 0);
} }
@@ -111,9 +96,10 @@ public class checkboxType extends inputType {
public void add_individual_view(LinearLayout parent, dataType data){ public void add_individual_view(LinearLayout parent, RawDataType 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);
@@ -121,11 +107,7 @@ public class checkboxType extends inputType {
} }
public void add_compiled_view(LinearLayout parent, RawDataType[] data){
public static int[] colors = {0x7f00ff00, 0x7f7f0000};
public void add_compiled_view(LinearLayout parent, dataType[] data){
PieChart chart = new PieChart(parent.getContext()); PieChart chart = new PieChart(parent.getContext());
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams( FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
@@ -154,7 +136,7 @@ public class checkboxType extends inputType {
entries.add(new PieEntry((float) numFalse, "False")); entries.add(new PieEntry((float) numFalse, "False"));
PieDataSet pieDataSet = new PieDataSet(entries, name); PieDataSet pieDataSet = new PieDataSet(entries, name);
pieDataSet.setColors(colors); pieDataSet.setColors(checkbox_colors);
PieData pieData = new PieData(pieDataSet); PieData pieData = new PieData(pieDataSet);
chart.setDrawHoleEnabled(false); chart.setDrawHoleEnabled(false);
chart.setData(pieData); chart.setData(pieData);
@@ -165,7 +147,7 @@ public class checkboxType extends inputType {
public void add_history_view(LinearLayout parent, dataType[] data){ public void add_history_view(LinearLayout parent, RawDataType[] data){
LineChart chart = new LineChart(parent.getContext()); LineChart chart = new LineChart(parent.getContext());
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams( FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
@@ -173,7 +155,7 @@ public class checkboxType extends inputType {
); );
layout.height = 350; layout.height = 350;
chart.setLayoutParams(layout); chart.setLayoutParams(layout);
chart.setBackgroundColor(0xff252025); chart.setBackgroundColor(chart_background);
LineData lineData = new LineData(); LineData lineData = new LineData();
@@ -189,11 +171,11 @@ public class checkboxType extends inputType {
} }
LineDataSet dataSet = new LineDataSet(entries, "is checked"); LineDataSet dataSet = new LineDataSet(entries, "is checked");
dataSet.setColor(Color.RED); dataSet.setColor(checkbox_data_color);
dataSet.setValueTextColor(Color.BLACK); dataSet.setValueTextColor(Color.BLACK);
dataSet.setDrawCircles(false); dataSet.setDrawCircles(false);
dataSet.setDrawValues(false); dataSet.setDrawValues(false);
dataSet.setValueTextColor(Color.RED); dataSet.setValueTextColor(checkbox_value_text_color);
lineData.addDataSet(dataSet); lineData.addDataSet(dataSet);
@@ -224,5 +206,13 @@ public class checkboxType extends inputType {
chart.invalidate(); chart.invalidate();
parent.addView(chart); parent.addView(chart);
} }
public void addDataToTable(TableLayout parent, Map<Integer, List<RawDataType>> data){
}
public String toString(RawDataType data){
return (int) data.get() == 1 ? "true" : "false";
}
} }
@@ -1,5 +1,10 @@
package com.ridgebotics.ridgescout.types.input; package com.ridgebotics.ridgescout.types.input;
import static com.ridgebotics.ridgescout.utility.Colors.chart_background;
import static com.ridgebotics.ridgescout.utility.Colors.chart_text;
import static com.ridgebotics.ridgescout.utility.Colors.dropdown_value_text_1;
import static com.ridgebotics.ridgescout.utility.Colors.dropdown_value_text_2;
import android.content.Context; import android.content.Context;
import android.graphics.Color; import android.graphics.Color;
import android.view.Gravity; import android.view.Gravity;
@@ -7,12 +12,12 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TableLayout;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.Nullable; import com.ridgebotics.ridgescout.types.data.RawDataType;
import com.ridgebotics.ridgescout.types.data.IntType;
import com.ridgebotics.ridgescout.types.data.dataType; import com.ridgebotics.ridgescout.ui.views.CustomSpinnerView;
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.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.charts.LineChart;
@@ -24,98 +29,57 @@ 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.ridgebotics.ridgescout.utility.builders.TextViewBuilder;
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.Map;
import java.util.function.Function; import java.util.function.Function;
public class dropdownType extends inputType { public class DropdownType extends FieldType {
public String[] text_options; public String[] text_options;
public int get_byte_id() {return dropdownType;} public int get_byte_id() {return dropdownType;}
public inputTypes getInputType(){return inputTypes.DROPDOWN;} public inputTypes getInputType(){return inputTypes.DROPDOWN;}
public dataType.valueTypes getValueType(){return dataType.valueTypes.NUM;} public RawDataType.valueTypes getValueType(){return RawDataType.valueTypes.NUM;}
public Object get_fallback_value(){return 0;} public Object get_fallback_value(){return 0;}
public 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 UUID, String name, String description, String[] text_options, int defaultSelIndex){
super(name); super(UUID, name, description);
this.text_options = text_options; this.text_options = text_options;
this.default_value = defaultSelIndex; this.default_value = defaultSelIndex;
} }
public byte[] encode() throws ByteBuilder.buildingException { public void encodeData(ByteBuilder bb) throws ByteBuilder.buildingException {
ByteBuilder bb = new ByteBuilder();
bb.addString(name);
bb.addInt((int)default_value); bb.addInt((int)default_value);
bb.addStringArray(text_options); bb.addStringArray(text_options);
return bb.build();
} }
public void decode(byte[] bytes) throws BuiltByteParser.byteParsingExeption { public void decodeData(ArrayList<BuiltByteParser.parsedObject> objects) {
BuiltByteParser bbp = new BuiltByteParser(bytes); default_value = objects.get(0).get();
ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse(); text_options = (String[]) objects.get(1).get();
name = (String) objects.get(0).get();
default_value = objects.get(1).get();
text_options = (String[]) objects.get(2).get();
} }
public PowerSpinnerView dropdown = null; public CustomSpinnerView dropdown = null;
public View createView(Context context, Function<dataType, Integer> onUpdate){ public View createView(Context context, Function<RawDataType, 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;
} }
public void setViewValue(Object value) { public void setViewValue(Object value) {
if(dropdown == null) return; if(dropdown == null) return;
if(intType.isNull((int) value)){ if(IntType.isNull((int) value)){
nullify(); nullify();
return; return;
} }
@@ -123,16 +87,16 @@ 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;
dropdown.setVisibility(View.GONE); dropdown.setVisibility(View.GONE);
} }
public dataType getViewValue(){ public RawDataType 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());
} }
@@ -140,18 +104,16 @@ public class dropdownType extends inputType {
public void add_individual_view(LinearLayout parent, dataType data){ public void add_individual_view(LinearLayout parent, RawDataType data){
if(data.isNull()) return; if(data.isNull()) return;
TextView tv = new TextView(parent.getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams( parent.addView(
ViewGroup.LayoutParams.MATCH_PARENT, new TextViewBuilder(parent.getContext(), text_options[(int) data.get()])
ViewGroup.LayoutParams.WRAP_CONTENT .layout_match_wrap()
)); .padding(20)
tv.setPadding(20,20,20,20); .size(18)
tv.setGravity(Gravity.CENTER_HORIZONTAL); .align_center()
tv.setText(text_options[(int) data.get()]); .build());
tv.setTextSize(18);
parent.addView(tv);
} }
@@ -176,7 +138,7 @@ public class dropdownType extends inputType {
return colors; return colors;
} }
public void add_compiled_view(LinearLayout parent, dataType[] data){ public void add_compiled_view(LinearLayout parent, RawDataType[] data){
PieChart chart = new PieChart(parent.getContext()); PieChart chart = new PieChart(parent.getContext());
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams( FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
@@ -184,7 +146,7 @@ public class dropdownType extends inputType {
); );
layout.height = 350; layout.height = 350;
chart.setLayoutParams(layout); chart.setLayoutParams(layout);
chart.setBackgroundColor(0xff252025); chart.setBackgroundColor(chart_background);
parent.addView(chart); parent.addView(chart);
int[] data_2 = new int[text_options.length]; int[] data_2 = new int[text_options.length];
@@ -210,7 +172,7 @@ public class dropdownType extends inputType {
public void add_history_view(LinearLayout parent, dataType[] data){ public void add_history_view(LinearLayout parent, RawDataType[] data){
LineChart chart = new LineChart(parent.getContext()); LineChart chart = new LineChart(parent.getContext());
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams( FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
@@ -218,7 +180,7 @@ public class dropdownType extends inputType {
); );
layout.height = 350; layout.height = 350;
chart.setLayoutParams(layout); chart.setLayoutParams(layout);
chart.setBackgroundColor(0xff252025); chart.setBackgroundColor(chart_background);
@@ -240,10 +202,10 @@ public class dropdownType extends inputType {
LineDataSet dataSet = new LineDataSet(entries, text_options[i]); LineDataSet dataSet = new LineDataSet(entries, text_options[i]);
dataSet.setColor(colors[i]); dataSet.setColor(colors[i]);
dataSet.setValueTextColor(Color.BLACK); dataSet.setValueTextColor(dropdown_value_text_1);
dataSet.setDrawCircles(false); dataSet.setDrawCircles(false);
dataSet.setDrawValues(false); dataSet.setDrawValues(false);
dataSet.setValueTextColor(Color.RED); dataSet.setValueTextColor(dropdown_value_text_2);
lineData.addDataSet(dataSet); lineData.addDataSet(dataSet);
} }
@@ -259,9 +221,9 @@ public class dropdownType extends inputType {
chart.setScaleEnabled(false); chart.setScaleEnabled(false);
chart.getXAxis().setTextColor(Color.WHITE); chart.getXAxis().setTextColor(chart_text);
chart.getAxisLeft().setTextColor(Color.WHITE); chart.getAxisLeft().setTextColor(chart_text);
chart.getAxisRight().setTextColor(Color.WHITE); chart.getAxisRight().setTextColor(chart_text);
chart.getAxisLeft().setAxisMinimum(0.f); chart.getAxisLeft().setAxisMinimum(0.f);
chart.getAxisLeft().setAxisMaximum(1.f); chart.getAxisLeft().setAxisMaximum(1.f);
@@ -270,10 +232,18 @@ public class dropdownType extends inputType {
chart.getAxisRight().setAxisMaximum(1.f); chart.getAxisRight().setAxisMaximum(1.f);
Legend legend = chart.getLegend(); Legend legend = chart.getLegend();
legend.setTextColor(Color.WHITE); legend.setTextColor(chart_text);
chart.invalidate(); chart.invalidate();
parent.addView(chart); parent.addView(chart);
} }
public void addDataToTable(TableLayout parent, Map<Integer, List<RawDataType>> data){
}
public String toString(RawDataType data){
return text_options[(int) data.get()];
}
} }
@@ -0,0 +1,105 @@
package com.ridgebotics.ridgescout.types.input;
import android.content.Context;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TableLayout;
import com.ridgebotics.ridgescout.types.data.RawDataType;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
// Abstract class for fields.
public abstract class FieldType {
// Define what the IDS are for each type
public static final int slider_type_id = 255;
public static final int dropdownType = 254;
public static final int notesType = 253;
public static final int tallyType = 252;
public static final int numberType = 251;
public static final int checkboxType = 250;
public static final int fieldposType = 249;
public enum inputTypes {
SLIDER,
DROPDOWN,
NOTES_INPUT,
TALLY,
NUMBER,
CHECKBOX,
FIELDPOS
}
public String UUID;
public String name;
public String description;
public Object default_value;
public abstract inputTypes getInputType();
public abstract RawDataType.valueTypes getValueType();
public abstract Object get_fallback_value();
public abstract int get_byte_id();
public FieldType(){}
public FieldType(String UUID, String name, String description){
this.UUID = UUID;
this.name = name;
this.description = description;
}
public abstract String get_type_name();
public byte[] encode() throws ByteBuilder.buildingException {
ByteBuilder bb = new ByteBuilder();
bb.addString(UUID);
bb.addString(name);
bb.addString(description);
encodeData(bb);
return bb.build();
}
public abstract void encodeData(ByteBuilder byteBuilder) throws ByteBuilder.buildingException;
public void decode(byte[] bytes) throws BuiltByteParser.byteParsingExeption {
BuiltByteParser bbp = new BuiltByteParser(bytes);
ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse();
UUID = (String) objects.remove(0).get();
name = (String) objects.remove(0).get();
description = (String) objects.remove(0).get();
decodeData(objects);
}
public abstract void decodeData(ArrayList<BuiltByteParser.parsedObject> objects);
// public abstract dataType[] getConfig();
// public abstract void setConfig(dataType[] config);
public abstract View createView(Context context, Function<RawDataType, Integer> onUpdate);
public boolean isBlank = false;
public abstract void nullify();
public void setViewValue(RawDataType type){setViewValue(type.get());}
public abstract void setViewValue(Object value);
public abstract RawDataType getViewValue();
public abstract void add_individual_view(LinearLayout parent, RawDataType data);
public abstract void add_compiled_view(LinearLayout parent, RawDataType[] data);
public abstract void add_history_view(LinearLayout parent, RawDataType[] data);
public abstract void addDataToTable(TableLayout parent, Map<Integer, List<RawDataType>> data);
public abstract String toString(RawDataType data);
}
@@ -0,0 +1,242 @@
package com.ridgebotics.ridgescout.types.input;
import static com.ridgebotics.ridgescout.utility.Colors.chart_background;
import static com.ridgebotics.ridgescout.utility.Colors.chart_text;
import static com.ridgebotics.ridgescout.utility.Colors.dropdown_value_text_1;
import static com.ridgebotics.ridgescout.utility.Colors.dropdown_value_text_2;
import static com.ridgebotics.ridgescout.utility.Colors.fieldpos_data;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TableLayout;
import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.components.Legend;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.data.LineDataSet;
import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.types.data.RawDataType;
import com.ridgebotics.ridgescout.types.data.IntArrType;
import com.ridgebotics.ridgescout.ui.views.FieldPosView;
import com.ridgebotics.ridgescout.ui.views.MultiFieldPosView;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
public class FieldposType extends FieldType {
public static final FieldImage DEFAULT_FIELD_IMAGE = FieldImage.F2025;
public enum FieldImage {
F2025(0, "2025", R.drawable.field_2025, R.drawable.field_2025_flipped),
F2025_analogous(1, "2025 - analogous", R.drawable.field_2025_analogous);
public int index, resId_normal, resId_flipped;
public String name;
public boolean flippable;
FieldImage(int index, String name, int resId) {
this.index = index;
this.name = name;
this.resId_normal = resId;
this.resId_flipped = resId;
this.flippable = false;
}
FieldImage(int index, String name, int resId_normal, int resId_flipped) {
this.index = index;
this.name = name;
this.resId_normal = resId_normal;
this.resId_flipped = resId_flipped;
this.flippable = true;
}
public static FieldImage from_index(int index) {
return FieldImage.values()[index];
}
public int get_index() {
return this.index;
}
@Override
public String toString() {
return name;
}
}
public FieldImage fieldImage;
public int get_byte_id() {return fieldposType;}
public inputTypes getInputType(){return inputTypes.FIELDPOS;}
public RawDataType.valueTypes getValueType(){return RawDataType.valueTypes.NUM;}
public Object get_fallback_value(){return 0;}
public FieldposType(){}
public String get_type_name(){return "Field Pos";}
public FieldposType(String UUID, String name, String description, FieldImage fieldImage, int[] default_value){
super(UUID, name, description);
this.fieldImage = fieldImage;
this.default_value = default_value;
}
public void encodeData(ByteBuilder bb) throws ByteBuilder.buildingException {
bb.addInt(this.fieldImage.get_index());
bb.addIntArray((int[]) default_value);
}
public void decodeData(ArrayList<BuiltByteParser.parsedObject> objects) {
fieldImage = FieldImage.from_index((int) objects.get(0).get());
default_value = objects.get(1).get();
}
public FieldPosView field = null;
public View createView(Context context, Function<RawDataType, Integer> onUpdate){
field = new FieldPosView(context, pos -> {
onUpdate.apply(new IntArrType(name, pos));
});
setViewValue(default_value);
field.setFieldImage(fieldImage);
return field;
}
public void setViewValue(Object value) {
if(field == null) return;
if(IntArrType.isNull((int[]) value)){
nullify();
return;
}
if(((int[]) value)[0] == 255 && ((int[]) value)[1] == 255){
nullify();
return;
}
isBlank = false;
field.setVisibility(View.VISIBLE);
field.setPos((int[]) value);
}
public void nullify(){
isBlank = true;
field.setVisibility(View.GONE);
}
public RawDataType getViewValue(){
if(field == null) return null;
if(field.getVisibility() == View.GONE) return IntArrType.newNull(name);
return new IntArrType(name, field.getPos());
}
public void add_individual_view(LinearLayout parent, RawDataType data){
if(data.isNull()) return;
FieldPosView fp = new FieldPosView(parent.getContext());
fp.setEnabled(false);
fp.setPos((int[]) data.get());
fp.setFieldImage(this.fieldImage);
parent.addView(fp);
}
public void add_compiled_view(LinearLayout parent, RawDataType[] data){
MultiFieldPosView mfp = new MultiFieldPosView(parent.getContext());
for(int i = 0; i < data.length; i++){
if(data[i].isNull()) continue;
mfp.addPos((int[]) data[i].get());
}
mfp.setFieldImage(fieldImage);
parent.addView(mfp);
}
public void add_history_view(LinearLayout parent, RawDataType[] data){
LineChart chart = new LineChart(parent.getContext());
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
layout.height = 350;
chart.setLayoutParams(layout);
chart.setBackgroundColor(chart_background);
int min = 0;
int max = 255;
List<Entry> entries = new ArrayList<>();
for (int i = 0; i < data.length; i++){
if(data[i] == null) continue;
if(data[i].isNull()) continue;
entries.add(new Entry(i, 255-(float)((int[]) data[i].get())[1]));
}
LineDataSet dataSet = new LineDataSet(entries, "Field position Y value");
dataSet.setColor(fieldpos_data);
dataSet.setValueTextColor(dropdown_value_text_1);
dataSet.setDrawCircles(false);
dataSet.setDrawValues(false);
LineData lineData = new LineData(dataSet);
chart.setData(lineData);
chart.invalidate();
chart.getDescription().setEnabled(false);
chart.setTouchEnabled(false);
chart.setDragEnabled(false);
chart.setScaleEnabled(false);
dataSet.setValueTextColor(dropdown_value_text_2);
chart.getXAxis().setTextColor(chart_text);
chart.getAxisLeft().setTextColor(chart_text);
chart.getAxisRight().setTextColor(chart_text);
Legend legend = chart.getLegend();
legend.setTextColor(chart_text);
chart.getAxisLeft().setAxisMinimum(min);
chart.getAxisLeft().setAxisMaximum(max);
chart.getAxisRight().setAxisMinimum(min);
chart.getAxisRight().setAxisMaximum(max);
parent.addView(chart);
}
public void addDataToTable(TableLayout parent, Map<Integer, List<RawDataType>> data){
}
public String toString(RawDataType data){
int[] intarr = (int[]) data.get();
return "[" + intarr[0] + "," + intarr[1] + "]";
}
}
@@ -2,8 +2,13 @@ package com.ridgebotics.ridgescout.types.input;
import static android.text.InputType.TYPE_CLASS_NUMBER; import static android.text.InputType.TYPE_CLASS_NUMBER;
import static com.ridgebotics.ridgescout.utility.Colors.chart_background;
import static com.ridgebotics.ridgescout.utility.Colors.chart_text;
import static com.ridgebotics.ridgescout.utility.Colors.dropdown_value_text_1;
import static com.ridgebotics.ridgescout.utility.Colors.dropdown_value_text_2;
import static com.ridgebotics.ridgescout.utility.Colors.number_data;
import android.content.Context; import android.content.Context;
import android.graphics.Color;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.view.Gravity; import android.view.Gravity;
@@ -12,6 +17,7 @@ import android.view.ViewGroup;
import android.widget.EditText; import android.widget.EditText;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TableLayout;
import android.widget.TextView; import android.widget.TextView;
import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.charts.LineChart;
@@ -19,24 +25,26 @@ import com.github.mikephil.charting.components.Legend;
import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.data.LineDataSet; import com.github.mikephil.charting.data.LineDataSet;
import com.ridgebotics.ridgescout.types.data.dataType; import com.ridgebotics.ridgescout.types.data.RawDataType;
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.ridgebotics.ridgescout.utility.builders.TextViewBuilder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
public class numberType extends inputType { public class NumberType extends FieldType {
public int get_byte_id() {return numberType;} public int get_byte_id() {return numberType;}
public inputTypes getInputType(){return inputTypes.NUMBER;} public inputTypes getInputType(){return inputTypes.NUMBER;}
public dataType.valueTypes getValueType(){return dataType.valueTypes.NUM;} public RawDataType.valueTypes getValueType(){return RawDataType.valueTypes.NUM;}
public Object get_fallback_value(){return 0;} public Object get_fallback_value(){return 0;}
public 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 UUID, String name, String description, int default_value){
super(name); super(UUID, name, description);
this.default_value = default_value; this.default_value = default_value;
} }
@@ -44,18 +52,11 @@ public class numberType extends inputType {
public byte[] encode() throws ByteBuilder.buildingException { public void encodeData(ByteBuilder bb) throws ByteBuilder.buildingException {
ByteBuilder bb = new ByteBuilder();
bb.addString(name);
bb.addInt((int)default_value); bb.addInt((int)default_value);
return bb.build();
} }
public void decode(byte[] bytes) throws BuiltByteParser.byteParsingExeption { public void decodeData(ArrayList<BuiltByteParser.parsedObject> objects) {
BuiltByteParser bbp = new BuiltByteParser(bytes); default_value = objects.get(0).get();
ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse();
name = (String) objects.get(0).get();
default_value = objects.get(1).get();
} }
@@ -64,7 +65,7 @@ public class numberType extends inputType {
public EditText num = null; public EditText num = null;
public View createView(Context context, Function<dataType, Integer> onUpdate){ public View createView(Context context, Function<RawDataType, Integer> onUpdate){
num = new EditText(context); num = new EditText(context);
num.setInputType(TYPE_CLASS_NUMBER); num.setInputType(TYPE_CLASS_NUMBER);
num.addTextChangedListener(new TextWatcher() { num.addTextChangedListener(new TextWatcher() {
@@ -82,7 +83,7 @@ public class numberType extends inputType {
public void setViewValue(Object value) { public void setViewValue(Object value) {
if(num == null) return; if(num == null) return;
if(intType.isNull((int)value)){ if(IntType.isNull((int)value)){
nullify(); nullify();
return; return;
} }
@@ -95,39 +96,34 @@ public class numberType extends inputType {
isBlank = true; isBlank = true;
num.setVisibility(View.GONE); num.setVisibility(View.GONE);
} }
public dataType getViewValue(){ public RawDataType getViewValue(){
if(num == null) return null; if(num == null) return null;
if(num.getVisibility() == View.GONE) return intType.newNull(name); if(num.getVisibility() == View.GONE) return IntType.newNull(name);
return new intType(name, safeToInt(num.getText().toString())); return new IntType(name, safeToInt(num.getText().toString()));
} }
private int safeToInt(String num){ private int safeToInt(String num){
if(num.isEmpty()) if(num.isEmpty())
return intType.nullval; return IntType.nullval;
try { try {
return Integer.parseInt(num); return Integer.parseInt(num);
}catch (NumberFormatException e){ }catch (NumberFormatException e){
return intType.nullval; return IntType.nullval;
} }
} }
public void add_individual_view(LinearLayout parent, dataType data){ public void add_individual_view(LinearLayout parent, RawDataType data){
if(data.isNull()) return; if(data.isNull()) return;
parent.addView(new TextViewBuilder(parent.getContext(), String.valueOf((int) data.get()))
TextView tv = new TextView(parent.getContext()); .layout_match_wrap()
tv.setLayoutParams(new FrameLayout.LayoutParams( .align_center()
ViewGroup.LayoutParams.MATCH_PARENT, .size(24)
ViewGroup.LayoutParams.WRAP_CONTENT .build());
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText(String.valueOf((int) data.get()));
tv.setTextSize(24);
parent.addView(tv);
} }
@@ -163,7 +159,7 @@ public class numberType extends inputType {
return entries; return entries;
} }
private static int findMin(dataType[] data){ private static int findMin(RawDataType[] data){
int min = (int)data[0].get(); int min = (int)data[0].get();
for(int i = 1; i < data.length; i++) for(int i = 1; i < data.length; i++)
if((int)data[i].get() < min) if((int)data[i].get() < min)
@@ -171,7 +167,7 @@ public class numberType extends inputType {
return min; return min;
} }
private static int findMax(dataType[] data){ private static int findMax(RawDataType[] data){
int max = (int)data[0].get(); int max = (int)data[0].get();
for(int i = 1; i < data.length; i++) for(int i = 1; i < data.length; i++)
if((int)data[i].get() > max) if((int)data[i].get() > max)
@@ -179,7 +175,7 @@ public class numberType extends inputType {
return max; return max;
} }
public void add_compiled_view(LinearLayout parent, dataType[] data){ public void add_compiled_view(LinearLayout parent, RawDataType[] data){
LineChart chart = new LineChart(parent.getContext()); LineChart chart = new LineChart(parent.getContext());
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams( FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
@@ -187,7 +183,7 @@ public class numberType extends inputType {
); );
layout.height = 350; layout.height = 350;
chart.setLayoutParams(layout); chart.setLayoutParams(layout);
chart.setBackgroundColor(0xff252025); chart.setBackgroundColor(chart_background);
int min = findMin(data); int min = findMin(data);
int max = findMax(data); int max = findMax(data);
@@ -212,8 +208,8 @@ public class numberType extends inputType {
LineDataSet dataSet = new LineDataSet(entries, name); LineDataSet dataSet = new LineDataSet(entries, name);
dataSet.setColor(Color.BLUE); dataSet.setColor(number_data);
dataSet.setValueTextColor(Color.BLACK); dataSet.setValueTextColor(dropdown_value_text_1);
dataSet.setDrawCircles(false); dataSet.setDrawCircles(false);
dataSet.setDrawValues(false); dataSet.setDrawValues(false);
@@ -228,7 +224,7 @@ public class numberType extends inputType {
LineDataSet normalDistSet = new LineDataSet(normalDistEntries, "Normal Distribution"); LineDataSet normalDistSet = new LineDataSet(normalDistEntries, "Normal Distribution");
normalDistSet.setColor(Color.RED); normalDistSet.setColor(dropdown_value_text_2);
normalDistSet.setDrawCircles(false); normalDistSet.setDrawCircles(false);
normalDistSet.setDrawValues(false); normalDistSet.setDrawValues(false);
normalDistSet.setLineWidth(2f); normalDistSet.setLineWidth(2f);
@@ -243,14 +239,14 @@ public class numberType extends inputType {
chart.setDragEnabled(false); chart.setDragEnabled(false);
chart.setScaleEnabled(false); chart.setScaleEnabled(false);
dataSet.setValueTextColor(Color.RED); dataSet.setValueTextColor(dropdown_value_text_2);
chart.getXAxis().setTextColor(Color.WHITE); chart.getXAxis().setTextColor(chart_text);
chart.getAxisLeft().setTextColor(Color.WHITE); chart.getAxisLeft().setTextColor(chart_text);
chart.getAxisRight().setTextColor(Color.WHITE); chart.getAxisRight().setTextColor(chart_text);
Legend legend = chart.getLegend(); Legend legend = chart.getLegend();
legend.setTextColor(Color.WHITE); legend.setTextColor(chart_text);
parent.addView(chart); parent.addView(chart);
} }
@@ -258,7 +254,7 @@ public class numberType extends inputType {
public void add_history_view(LinearLayout parent, dataType[] data){ public void add_history_view(LinearLayout parent, RawDataType[] data){
LineChart chart = new LineChart(parent.getContext()); LineChart chart = new LineChart(parent.getContext());
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams( FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
@@ -266,7 +262,7 @@ public class numberType extends inputType {
); );
layout.height = 350; layout.height = 350;
chart.setLayoutParams(layout); chart.setLayoutParams(layout);
chart.setBackgroundColor(0xff252025); chart.setBackgroundColor(chart_background);
int min = findMin(data); int min = findMin(data);
int max = findMax(data); int max = findMax(data);
@@ -281,8 +277,8 @@ public class numberType extends inputType {
LineDataSet dataSet = new LineDataSet(entries, name); LineDataSet dataSet = new LineDataSet(entries, name);
dataSet.setColor(Color.BLUE); dataSet.setColor(number_data);
dataSet.setValueTextColor(Color.BLACK); dataSet.setValueTextColor(dropdown_value_text_1);
dataSet.setDrawCircles(false); dataSet.setDrawCircles(false);
dataSet.setDrawValues(false); dataSet.setDrawValues(false);
@@ -296,14 +292,14 @@ public class numberType extends inputType {
chart.setDragEnabled(false); chart.setDragEnabled(false);
chart.setScaleEnabled(false); chart.setScaleEnabled(false);
dataSet.setValueTextColor(Color.RED); dataSet.setValueTextColor(dropdown_value_text_2);
chart.getXAxis().setTextColor(Color.WHITE); chart.getXAxis().setTextColor(chart_text);
chart.getAxisLeft().setTextColor(Color.WHITE); chart.getAxisLeft().setTextColor(chart_text);
chart.getAxisRight().setTextColor(Color.WHITE); chart.getAxisRight().setTextColor(chart_text);
Legend legend = chart.getLegend(); Legend legend = chart.getLegend();
legend.setTextColor(Color.WHITE); legend.setTextColor(chart_text);
chart.getAxisLeft().setAxisMinimum(min); chart.getAxisLeft().setAxisMinimum(min);
@@ -315,5 +311,13 @@ public class numberType extends inputType {
parent.addView(chart); parent.addView(chart);
} }
public void addDataToTable(TableLayout parent, Map<Integer, List<RawDataType>> data){
}
public String toString(RawDataType data){
return String.valueOf((int) data.get());
}
} }
@@ -1,16 +1,22 @@
package com.ridgebotics.ridgescout.types.input; package com.ridgebotics.ridgescout.types.input;
import static com.ridgebotics.ridgescout.utility.Colors.chart_background;
import static com.ridgebotics.ridgescout.utility.Colors.chart_text;
import static com.ridgebotics.ridgescout.utility.Colors.dropdown_value_text_1;
import static com.ridgebotics.ridgescout.utility.Colors.dropdown_value_text_2;
import static com.ridgebotics.ridgescout.utility.Colors.slider_data;
import android.content.Context; import android.content.Context;
import android.graphics.Color;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TableLayout;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import com.ridgebotics.ridgescout.types.data.dataType; import com.ridgebotics.ridgescout.types.data.RawDataType;
import com.ridgebotics.ridgescout.types.data.intType; import com.ridgebotics.ridgescout.types.data.IntType;
import com.ridgebotics.ridgescout.utility.AlertManager; import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.BuiltByteParser; import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder; import com.ridgebotics.ridgescout.utility.ByteBuilder;
@@ -23,20 +29,21 @@ import com.google.android.material.slider.Slider;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
public class sliderType extends inputType { public class SliderType extends FieldType {
// public int defaultValue; // public int defaultValue;
public int min; public int min;
public int max; public int max;
public int get_byte_id() {return slider_type_id;} public int get_byte_id() {return slider_type_id;}
public inputTypes getInputType(){return inputTypes.SLIDER;} public inputTypes getInputType(){return inputTypes.SLIDER;}
public dataType.valueTypes getValueType(){return dataType.valueTypes.NUM;} public RawDataType.valueTypes getValueType(){return RawDataType.valueTypes.NUM;}
public Object get_fallback_value(){return 0;} public Object get_fallback_value(){return 0;}
public 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 UUID, String name, String description, int defaultValue, int min, int max){
super(name); super(UUID, name, description);
this.default_value = defaultValue; this.default_value = defaultValue;
this.min = min; this.min = min;
this.max = max; this.max = max;
@@ -45,22 +52,15 @@ public class sliderType extends inputType {
public byte[] encode() throws ByteBuilder.buildingException { public void encodeData(ByteBuilder bb) throws ByteBuilder.buildingException {
ByteBuilder bb = new ByteBuilder();
bb.addString(name);
bb.addInt((int) default_value); bb.addInt((int) default_value);
bb.addInt(min); bb.addInt(min);
bb.addInt(max); bb.addInt(max);
return bb.build();
} }
public void decode(byte[] bytes) throws BuiltByteParser.byteParsingExeption { public void decodeData(ArrayList<BuiltByteParser.parsedObject> objects) {
BuiltByteParser bbp = new BuiltByteParser(bytes); default_value = objects.get(0).get();
ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse(); min = (int) objects.get(1).get();
max = (int) objects.get(2).get();
name = (String) objects.get(0).get();
default_value = objects.get(1).get();
min = (int) objects.get(2).get();
max = (int) objects.get(3).get();
} }
@@ -68,7 +68,7 @@ public class sliderType extends inputType {
public Slider slider = null; public Slider slider = null;
public View createView(Context context, Function<dataType, Integer> onUpdate){ public View createView(Context context, Function<RawDataType, Integer> onUpdate){
slider = new Slider(context); slider = new Slider(context);
setViewValue(default_value); setViewValue(default_value);
slider.setStepSize((float) 1 / (max-min)); slider.setStepSize((float) 1 / (max-min));
@@ -83,7 +83,7 @@ public class sliderType extends inputType {
public void setViewValue(Object value) { public void setViewValue(Object value) {
if(slider == null) return; if(slider == null) return;
if(intType.isNull((int) value)){ if(IntType.isNull((int) value)){
nullify(); nullify();
return; return;
} }
@@ -94,7 +94,7 @@ public class sliderType extends inputType {
float slidervalue = round_position*step_size; float slidervalue = round_position*step_size;
if(slidervalue > 1 || slidervalue < 0) { if(slidervalue > 1 || slidervalue < 0) {
AlertManager.error("Error loading slider " + name); AlertManager.addSimpleError("Error loading slider " + name);
slider.setValue(0); slider.setValue(0);
}else{ }else{
slider.setValue(slidervalue); slider.setValue(slidervalue);
@@ -103,10 +103,10 @@ public class sliderType extends inputType {
slider.setVisibility(View.VISIBLE); slider.setVisibility(View.VISIBLE);
} }
public dataType getViewValue(){ public RawDataType getViewValue(){
if(slider == null) return null; if(slider == null) return null;
if(slider.getVisibility() == View.GONE) return intType.newNull(name); if(slider.getVisibility() == View.GONE) return IntType.newNull(name);
return new intType(name, min + (int) (slider.getValue() * (max-min))); return new IntType(name, min + (int) (slider.getValue() * (max-min)));
} }
public void nullify(){ public void nullify(){
isBlank = true; isBlank = true;
@@ -118,7 +118,7 @@ public class sliderType extends inputType {
public void add_individual_view(LinearLayout parent, dataType data){ public void add_individual_view(LinearLayout parent, RawDataType data){
if(data.isNull()) return; if(data.isNull()) return;
Slider slider = new Slider(parent.getContext()); Slider slider = new Slider(parent.getContext());
@@ -127,7 +127,7 @@ public class sliderType extends inputType {
int round_position = Math.round(slider_position / step_size); int round_position = Math.round(slider_position / step_size);
float value = round_position*step_size; float value = round_position*step_size;
if(value > 1 || value < 0) { if(value > 1 || value < 0) {
AlertManager.error("Error loading slider " + name); AlertManager.addSimpleError("Error loading slider " + name);
slider.setValue(0); slider.setValue(0);
}else{ }else{
slider.setValue(value); slider.setValue(value);
@@ -172,7 +172,7 @@ public class sliderType extends inputType {
return entries; return entries;
} }
public void add_compiled_view(LinearLayout parent, dataType[] data){ public void add_compiled_view(LinearLayout parent, RawDataType[] data){
LineChart chart = new LineChart(parent.getContext()); LineChart chart = new LineChart(parent.getContext());
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams( FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
@@ -180,7 +180,7 @@ public class sliderType extends inputType {
); );
layout.height = 350; layout.height = 350;
chart.setLayoutParams(layout); chart.setLayoutParams(layout);
chart.setBackgroundColor(0xff252025); chart.setBackgroundColor(chart_background);
int[] values = new int[max-min+1]; int[] values = new int[max-min+1];
@@ -202,8 +202,8 @@ public class sliderType extends inputType {
LineDataSet dataSet = new LineDataSet(entries, name); LineDataSet dataSet = new LineDataSet(entries, name);
dataSet.setColor(Color.BLUE); dataSet.setColor(slider_data);
dataSet.setValueTextColor(Color.BLACK); dataSet.setValueTextColor(dropdown_value_text_1);
dataSet.setDrawCircles(false); dataSet.setDrawCircles(false);
dataSet.setDrawValues(false); dataSet.setDrawValues(false);
@@ -218,7 +218,7 @@ public class sliderType extends inputType {
LineDataSet normalDistSet = new LineDataSet(normalDistEntries, "Normal Distribution"); LineDataSet normalDistSet = new LineDataSet(normalDistEntries, "Normal Distribution");
normalDistSet.setColor(Color.RED); normalDistSet.setColor(dropdown_value_text_2);
normalDistSet.setDrawCircles(false); normalDistSet.setDrawCircles(false);
normalDistSet.setDrawValues(false); normalDistSet.setDrawValues(false);
normalDistSet.setLineWidth(2f); normalDistSet.setLineWidth(2f);
@@ -233,14 +233,14 @@ public class sliderType extends inputType {
chart.setDragEnabled(false); chart.setDragEnabled(false);
chart.setScaleEnabled(false); chart.setScaleEnabled(false);
dataSet.setValueTextColor(Color.RED); dataSet.setValueTextColor(dropdown_value_text_2);
chart.getXAxis().setTextColor(Color.WHITE); chart.getXAxis().setTextColor(chart_text);
chart.getAxisLeft().setTextColor(Color.WHITE); chart.getAxisLeft().setTextColor(chart_text);
chart.getAxisRight().setTextColor(Color.WHITE); chart.getAxisRight().setTextColor(chart_text);
Legend legend = chart.getLegend(); Legend legend = chart.getLegend();
legend.setTextColor(Color.WHITE); legend.setTextColor(chart_text);
parent.addView(chart); parent.addView(chart);
} }
@@ -248,7 +248,7 @@ public class sliderType extends inputType {
public void add_history_view(LinearLayout parent, dataType[] data){ public void add_history_view(LinearLayout parent, RawDataType[] data){
LineChart chart = new LineChart(parent.getContext()); LineChart chart = new LineChart(parent.getContext());
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams( FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
@@ -256,7 +256,7 @@ public class sliderType extends inputType {
); );
layout.height = 350; layout.height = 350;
chart.setLayoutParams(layout); chart.setLayoutParams(layout);
chart.setBackgroundColor(0xff252025); chart.setBackgroundColor(chart_background);
List<Entry> entries = new ArrayList<>(); List<Entry> entries = new ArrayList<>();
for (int i = 0; i < data.length; i++){ for (int i = 0; i < data.length; i++){
@@ -268,8 +268,8 @@ public class sliderType extends inputType {
LineDataSet dataSet = new LineDataSet(entries, name); LineDataSet dataSet = new LineDataSet(entries, name);
dataSet.setColor(Color.BLUE); dataSet.setColor(slider_data);
dataSet.setValueTextColor(Color.BLACK); dataSet.setValueTextColor(dropdown_value_text_1);
dataSet.setDrawCircles(false); dataSet.setDrawCircles(false);
dataSet.setDrawValues(false); dataSet.setDrawValues(false);
@@ -283,14 +283,14 @@ public class sliderType extends inputType {
chart.setDragEnabled(false); chart.setDragEnabled(false);
chart.setScaleEnabled(false); chart.setScaleEnabled(false);
dataSet.setValueTextColor(Color.RED); dataSet.setValueTextColor(dropdown_value_text_2);
chart.getXAxis().setTextColor(Color.WHITE); chart.getXAxis().setTextColor(chart_text);
chart.getAxisLeft().setTextColor(Color.WHITE); chart.getAxisLeft().setTextColor(chart_text);
chart.getAxisRight().setTextColor(Color.WHITE); chart.getAxisRight().setTextColor(chart_text);
Legend legend = chart.getLegend(); Legend legend = chart.getLegend();
legend.setTextColor(Color.WHITE); legend.setTextColor(chart_text);
chart.getAxisLeft().setAxisMinimum(min); chart.getAxisLeft().setAxisMinimum(min);
@@ -302,4 +302,12 @@ public class sliderType extends inputType {
parent.addView(chart); parent.addView(chart);
} }
public void addDataToTable(TableLayout parent, Map<Integer, List<RawDataType>> data){
}
public String toString(RawDataType data){
return String.valueOf((int) data.get());
}
} }
@@ -1,17 +1,27 @@
package com.ridgebotics.ridgescout.types.input; package com.ridgebotics.ridgescout.types.input;
import static com.ridgebotics.ridgescout.utility.Colors.chart_background;
import static com.ridgebotics.ridgescout.utility.Colors.chart_text;
import static com.ridgebotics.ridgescout.utility.Colors.dropdown_value_text_1;
import static com.ridgebotics.ridgescout.utility.Colors.dropdown_value_text_2;
import static com.ridgebotics.ridgescout.utility.Colors.tally_data;
import android.content.Context; import android.content.Context;
import android.graphics.Color;
import android.view.Gravity; import android.view.Gravity;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView; import android.widget.TextView;
import com.ridgebotics.ridgescout.types.data.dataType; import com.ridgebotics.ridgescout.types.data.RawDataType;
import com.ridgebotics.ridgescout.types.data.intType; import com.ridgebotics.ridgescout.types.data.IntType;
import com.ridgebotics.ridgescout.ui.scouting.TallyCounterView; import com.ridgebotics.ridgescout.ui.views.CandlestickHeader;
import com.ridgebotics.ridgescout.ui.views.CandlestickView;
import com.ridgebotics.ridgescout.ui.data.DataProcessing;
import com.ridgebotics.ridgescout.ui.views.TallyCounterView;
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;
@@ -19,20 +29,23 @@ import com.github.mikephil.charting.components.Legend;
import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.data.LineDataSet; import com.github.mikephil.charting.data.LineDataSet;
import com.ridgebotics.ridgescout.utility.builders.TextViewBuilder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
public class tallyType extends inputType { public class TallyType extends FieldType {
public int get_byte_id() {return tallyType;} public int get_byte_id() {return tallyType;}
public inputTypes getInputType(){return inputTypes.TALLY;} public inputTypes getInputType(){return inputTypes.TALLY;}
public dataType.valueTypes getValueType(){return dataType.valueTypes.NUM;} public RawDataType.valueTypes getValueType(){return RawDataType.valueTypes.NUM;}
public Object get_fallback_value(){return 0;} public Object get_fallback_value(){return 0;}
public 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 UUID, String name, String description, int default_value){
super(name); super(UUID, name, description);
this.default_value = default_value; this.default_value = default_value;
} }
@@ -40,18 +53,11 @@ public class tallyType extends inputType {
public byte[] encode() throws ByteBuilder.buildingException { public void encodeData(ByteBuilder bb) throws ByteBuilder.buildingException {
ByteBuilder bb = new ByteBuilder();
bb.addString(name);
bb.addInt((int) default_value); bb.addInt((int) default_value);
return bb.build();
} }
public void decode(byte[] bytes) throws BuiltByteParser.byteParsingExeption { public void decodeData(ArrayList<BuiltByteParser.parsedObject> objects) {
BuiltByteParser bbp = new BuiltByteParser(bytes); default_value = objects.get(0).get();
ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse();
name = (String) objects.get(0).get();
default_value = objects.get(1).get();
} }
@@ -60,7 +66,7 @@ public class tallyType extends inputType {
public TallyCounterView tally = null; public TallyCounterView tally = null;
public View createView(Context context, Function<dataType, Integer> onUpdate){ public View createView(Context context, Function<RawDataType, Integer> onUpdate){
tally = new TallyCounterView(context); tally = new TallyCounterView(context);
tally.setOnCountChangedListener(n -> onUpdate.apply(getViewValue())); tally.setOnCountChangedListener(n -> onUpdate.apply(getViewValue()));
@@ -72,8 +78,7 @@ 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;
} }
@@ -86,10 +91,10 @@ public class tallyType extends inputType {
isBlank = true; isBlank = true;
tally.setVisibility(View.GONE); tally.setVisibility(View.GONE);
} }
public dataType getViewValue(){ public RawDataType getViewValue(){
if(tally == null) return null; if(tally == null) return null;
if(tally.getVisibility() == View.GONE) return intType.newNull(name); if(tally.getVisibility() == View.GONE) return IntType.newNull(name);
return new intType(name, tally.getValue()); return new IntType(name, tally.getValue());
} }
@@ -97,18 +102,13 @@ public class tallyType extends inputType {
public void add_individual_view(LinearLayout parent, dataType data){ public void add_individual_view(LinearLayout parent, RawDataType data){
if(data.isNull()) return; if(data.isNull()) return;
parent.addView(new TextViewBuilder(parent.getContext(), String.valueOf((int) data.get()))
TextView tv = new TextView(parent.getContext()); .layout_match_wrap()
tv.setLayoutParams(new FrameLayout.LayoutParams( .align_center()
ViewGroup.LayoutParams.MATCH_PARENT, .size(24)
ViewGroup.LayoutParams.WRAP_CONTENT .build());
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText(String.valueOf((int) data.get()));
tv.setTextSize(24);
parent.addView(tv);
} }
@@ -144,7 +144,7 @@ public class tallyType extends inputType {
return entries; return entries;
} }
private static int findMin(dataType[] data){ private static int findMin(RawDataType[] data){
int min = (int)data[0].get(); int min = (int)data[0].get();
for(int i = 1; i < data.length; i++) for(int i = 1; i < data.length; i++)
if((int)data[i].get() < min) if((int)data[i].get() < min)
@@ -152,7 +152,7 @@ public class tallyType extends inputType {
return min; return min;
} }
private static int findMax(dataType[] data){ private static int findMax(RawDataType[] data){
int max = (int)data[0].get(); int max = (int)data[0].get();
for(int i = 1; i < data.length; i++) for(int i = 1; i < data.length; i++)
if((int)data[i].get() > max) if((int)data[i].get() > max)
@@ -160,7 +160,7 @@ public class tallyType extends inputType {
return max; return max;
} }
public void add_compiled_view(LinearLayout parent, dataType[] data){ public void add_compiled_view(LinearLayout parent, RawDataType[] data){
LineChart chart = new LineChart(parent.getContext()); LineChart chart = new LineChart(parent.getContext());
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams( FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
@@ -193,8 +193,8 @@ public class tallyType extends inputType {
LineDataSet dataSet = new LineDataSet(entries, name); LineDataSet dataSet = new LineDataSet(entries, name);
dataSet.setColor(Color.BLUE); dataSet.setColor(tally_data);
dataSet.setValueTextColor(Color.BLACK); dataSet.setValueTextColor(dropdown_value_text_1);
dataSet.setDrawCircles(false); dataSet.setDrawCircles(false);
dataSet.setDrawValues(false); dataSet.setDrawValues(false);
@@ -209,7 +209,7 @@ public class tallyType extends inputType {
LineDataSet normalDistSet = new LineDataSet(normalDistEntries, "Normal Distribution"); LineDataSet normalDistSet = new LineDataSet(normalDistEntries, "Normal Distribution");
normalDistSet.setColor(Color.RED); normalDistSet.setColor(dropdown_value_text_2);
normalDistSet.setDrawCircles(false); normalDistSet.setDrawCircles(false);
normalDistSet.setDrawValues(false); normalDistSet.setDrawValues(false);
normalDistSet.setLineWidth(2f); normalDistSet.setLineWidth(2f);
@@ -224,14 +224,14 @@ public class tallyType extends inputType {
chart.setDragEnabled(false); chart.setDragEnabled(false);
chart.setScaleEnabled(false); chart.setScaleEnabled(false);
dataSet.setValueTextColor(Color.RED); dataSet.setValueTextColor(dropdown_value_text_2);
chart.getXAxis().setTextColor(Color.WHITE); chart.getXAxis().setTextColor(chart_text);
chart.getAxisLeft().setTextColor(Color.WHITE); chart.getAxisLeft().setTextColor(chart_text);
chart.getAxisRight().setTextColor(Color.WHITE); chart.getAxisRight().setTextColor(chart_text);
Legend legend = chart.getLegend(); Legend legend = chart.getLegend();
legend.setTextColor(Color.WHITE); legend.setTextColor(chart_text);
parent.addView(chart); parent.addView(chart);
} }
@@ -239,7 +239,7 @@ public class tallyType extends inputType {
public void add_history_view(LinearLayout parent, dataType[] data){ public void add_history_view(LinearLayout parent, RawDataType[] data){
LineChart chart = new LineChart(parent.getContext()); LineChart chart = new LineChart(parent.getContext());
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams( FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
@@ -247,7 +247,7 @@ public class tallyType extends inputType {
); );
layout.height = 350; layout.height = 350;
chart.setLayoutParams(layout); chart.setLayoutParams(layout);
chart.setBackgroundColor(0xff252025); chart.setBackgroundColor(chart_background);
int min = findMin(data); int min = findMin(data);
int max = findMax(data); int max = findMax(data);
@@ -262,8 +262,8 @@ public class tallyType extends inputType {
LineDataSet dataSet = new LineDataSet(entries, name); LineDataSet dataSet = new LineDataSet(entries, name);
dataSet.setColor(Color.BLUE); dataSet.setColor(tally_data);
dataSet.setValueTextColor(Color.BLACK); dataSet.setValueTextColor(dropdown_value_text_1);
dataSet.setDrawCircles(false); dataSet.setDrawCircles(false);
dataSet.setDrawValues(false); dataSet.setDrawValues(false);
@@ -277,14 +277,14 @@ public class tallyType extends inputType {
chart.setDragEnabled(false); chart.setDragEnabled(false);
chart.setScaleEnabled(false); chart.setScaleEnabled(false);
dataSet.setValueTextColor(Color.RED); dataSet.setValueTextColor(dropdown_value_text_2);
chart.getXAxis().setTextColor(Color.WHITE); chart.getXAxis().setTextColor(chart_text);
chart.getAxisLeft().setTextColor(Color.WHITE); chart.getAxisLeft().setTextColor(chart_text);
chart.getAxisRight().setTextColor(Color.WHITE); chart.getAxisRight().setTextColor(chart_text);
Legend legend = chart.getLegend(); Legend legend = chart.getLegend();
legend.setTextColor(Color.WHITE); legend.setTextColor(chart_text);
chart.getAxisLeft().setAxisMinimum(min); chart.getAxisLeft().setAxisMinimum(min);
@@ -296,5 +296,64 @@ public class tallyType extends inputType {
parent.addView(chart); parent.addView(chart);
} }
public void addDataToTable(TableLayout parent, Map<Integer, List<RawDataType>> data){
int[] tmp_abs_bounds = DataProcessing.getNumberBounds(data);
int absmin = tmp_abs_bounds[0];
int absmax = tmp_abs_bounds[1];
//(int[]) teamData.get(i).get())[0];
// AlertManager.alert("Results","Min: " + min + " Max: " + max);
parent.removeAllViews();
List<CandlestickView> views = new ArrayList<>();
for(Integer teamNum : data.keySet()){
CandlestickView candlestickView = new CandlestickView(parent.getContext());
candlestickView.fromTeamData(data.get(teamNum), teamNum, absmin, absmax);
views.add(candlestickView);
}
TableRow row = new TableRow(parent.getContext());
// Make candlestick chart fill full width
parent.setColumnStretchable(1, true);
// Fill in top left cell
row.addView(new View(parent.getContext()));
CandlestickHeader header = new CandlestickHeader(parent.getContext());
header.setScale(absmin, absmax);
row.addView(header);
parent.addView(row);
// parent.addView(new );
try {
Collections.sort(views, (a, b) -> (int) ((b.average - a.average) * 50.f));
}catch (Exception e){}
for(int i = 0; i < views.size(); i++){
row = new TableRow(parent.getContext());
CandlestickView view = views.get(i);
row.addView(new TextViewBuilder(parent.getContext(), String.valueOf(view.teamNum))
.align_center()
.padding(10)
.h6()
.build());
row.addView(view);
parent.addView(row);
}
}
public String toString(RawDataType data){
return String.valueOf((int) data.get());
}
} }
@@ -1,7 +1,12 @@
package com.ridgebotics.ridgescout.types.input; package com.ridgebotics.ridgescout.types.input;
import static com.ridgebotics.ridgescout.utility.Colors.chart_background;
import static com.ridgebotics.ridgescout.utility.Colors.chart_text;
import static com.ridgebotics.ridgescout.utility.Colors.dropdown_value_text_1;
import static com.ridgebotics.ridgescout.utility.Colors.dropdown_value_text_2;
import static com.ridgebotics.ridgescout.utility.Colors.text_data;
import android.content.Context; import android.content.Context;
import android.graphics.Color;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.view.Gravity; import android.view.Gravity;
@@ -10,10 +15,11 @@ import android.view.ViewGroup;
import android.widget.EditText; import android.widget.EditText;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TableLayout;
import android.widget.TextView; import android.widget.TextView;
import com.ridgebotics.ridgescout.types.data.dataType; import com.ridgebotics.ridgescout.types.data.RawDataType;
import com.ridgebotics.ridgescout.types.data.stringType; import com.ridgebotics.ridgescout.types.data.StringType;
import com.ridgebotics.ridgescout.utility.SentimentAnalysis; import com.ridgebotics.ridgescout.utility.SentimentAnalysis;
import com.ridgebotics.ridgescout.utility.BuiltByteParser; import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder; import com.ridgebotics.ridgescout.utility.ByteBuilder;
@@ -22,20 +28,22 @@ import com.github.mikephil.charting.components.Legend;
import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.data.LineDataSet; import com.github.mikephil.charting.data.LineDataSet;
import com.ridgebotics.ridgescout.utility.builders.TextViewBuilder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
public class textType extends inputType { public class TextType extends FieldType {
public int get_byte_id() {return notesType;} public int get_byte_id() {return notesType;}
public inputTypes getInputType(){return inputTypes.NOTES_INPUT;} public inputTypes getInputType(){return inputTypes.NOTES_INPUT;}
public dataType.valueTypes getValueType(){return dataType.valueTypes.STRING;} public RawDataType.valueTypes getValueType(){return RawDataType.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 UUID, String name, String description, String default_text){
super(name); super(UUID, 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";}
@@ -46,18 +54,11 @@ public class textType extends inputType {
public byte[] encode() throws ByteBuilder.buildingException { public void encodeData(ByteBuilder bb) throws ByteBuilder.buildingException {
ByteBuilder bb = new ByteBuilder();
bb.addString(name);
bb.addString((String) default_value); bb.addString((String) default_value);
return bb.build();
} }
public void decode(byte[] bytes) throws BuiltByteParser.byteParsingExeption { public void decodeData(ArrayList<BuiltByteParser.parsedObject> objects) {
BuiltByteParser bbp = new BuiltByteParser(bytes); default_value = objects.get(0).get();
ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse();
name = (String) objects.get(0).get();
default_value = objects.get(1).get();
} }
@@ -75,7 +76,7 @@ public class textType extends inputType {
public EditText text = null; public EditText text = null;
public View createView(Context context, Function<dataType, Integer> onUpdate){ public View createView(Context context, Function<RawDataType, Integer> onUpdate){
text = new EditText(context); text = new EditText(context);
text.setText((String)default_value); text.setText((String)default_value);
text.addTextChangedListener(new TextWatcher() { text.addTextChangedListener(new TextWatcher() {
@@ -89,7 +90,7 @@ public class textType extends inputType {
} }
public void setViewValue(Object value) { public void setViewValue(Object value) {
if(text == null) return; if(text == null) return;
if(stringType.isNull((String) value)){ if(StringType.isNull((String) value)){
nullify(); nullify();
return; return;
} }
@@ -101,26 +102,22 @@ public class textType extends inputType {
isBlank = true; isBlank = true;
text.setVisibility(View.GONE); text.setVisibility(View.GONE);
} }
public dataType getViewValue(){ public RawDataType getViewValue(){
if(text == null) return null; if(text == null) return null;
if(text.getVisibility() == View.GONE) return new stringType(name, stringType.nullval); if(text.getVisibility() == View.GONE) return new StringType(name, StringType.nullval);
return new stringType(name, text.getText().toString()); return new StringType(name, text.getText().toString());
} }
public void add_individual_view(LinearLayout parent, dataType data){ public void add_individual_view(LinearLayout parent, RawDataType data){
if(data.isNull()) return; if(data.isNull()) return;
TextView tv = new TextView(parent.getContext()); parent.addView(new TextViewBuilder(parent.getContext(), (String) data.get())
tv.setLayoutParams(new FrameLayout.LayoutParams( .layout_match_wrap()
ViewGroup.LayoutParams.MATCH_PARENT, .align_center()
ViewGroup.LayoutParams.WRAP_CONTENT .size(18)
)); .build());
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText((String) data.get());
tv.setTextSize(18);
parent.addView(tv);
} }
@@ -140,36 +137,31 @@ public class textType extends inputType {
TextView positive_text; TextView positive_text;
public void add_compiled_view(LinearLayout parent, dataType[] data) { public void add_compiled_view(LinearLayout parent, RawDataType[] data) {
positive_mean = 0; positive_mean = 0;
count = 0; count = 0;
positive_text = new TextView(parent.getContext()); positive_text = new TextViewBuilder(parent.getContext())
positive_text.setLayoutParams(new FrameLayout.LayoutParams( .align_center()
ViewGroup.LayoutParams.MATCH_PARENT, .size(20)
ViewGroup.LayoutParams.WRAP_CONTENT .build();
));
positive_text.setGravity(Gravity.CENTER_HORIZONTAL);
positive_text.setTextSize(20);
parent.addView(positive_text); parent.addView(positive_text);
for (int i = 0; i < data.length; i++){ for (int i = 0; i < data.length; i++){
if (!data[i].isNull()) { if (!data[i].isNull()) {
SentimentAnalysis.analyse((String) data[i].get(), new SentimentAnalysis.resultCallback() { SentimentAnalysis.analyse((String) data[i].get(), sentiment -> {
@Override
public void onFinish(float sentiment) {
positive_mean += sentiment; positive_mean += sentiment;
count++; count++;
positive_text.setText("Sentiment: " + (positive_mean / count)); positive_text.setText("Sentiment: " + (positive_mean / count));
}
}); });
} }
} }
} }
public void add_history_view(LinearLayout parent, dataType[] data){ public void add_history_view(LinearLayout parent, RawDataType[] data){
LineChart chart = new LineChart(parent.getContext()); LineChart chart = new LineChart(parent.getContext());
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams( FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
@@ -177,7 +169,7 @@ public class textType extends inputType {
); );
layout.height = 350; layout.height = 350;
chart.setLayoutParams(layout); chart.setLayoutParams(layout);
chart.setBackgroundColor(0xff252025); chart.setBackgroundColor(chart_background);
List<Entry> entries = new ArrayList<>(); List<Entry> entries = new ArrayList<>();
@@ -192,11 +184,11 @@ public class textType extends inputType {
); );
} }
LineDataSet dataSet = new LineDataSet(entries, "Sentiment"); LineDataSet dataSet = new LineDataSet(entries, "Sentiment");
dataSet.setColor(Color.BLUE); dataSet.setColor(text_data);
dataSet.setValueTextColor(Color.BLACK); dataSet.setValueTextColor(dropdown_value_text_1);
dataSet.setDrawCircles(false); dataSet.setDrawCircles(false);
dataSet.setDrawValues(false); dataSet.setDrawValues(false);
dataSet.setValueTextColor(Color.RED); dataSet.setValueTextColor(dropdown_value_text_2);
LineData lineData = new LineData(dataSet); LineData lineData = new LineData(dataSet);
@@ -210,9 +202,9 @@ public class textType extends inputType {
chart.setScaleEnabled(false); chart.setScaleEnabled(false);
chart.getXAxis().setTextColor(Color.WHITE); chart.getXAxis().setTextColor(chart_text);
chart.getAxisLeft().setTextColor(Color.WHITE); chart.getAxisLeft().setTextColor(chart_text);
chart.getAxisRight().setTextColor(Color.WHITE); chart.getAxisRight().setTextColor(chart_text);
chart.getAxisLeft().setAxisMinimum(0.f); chart.getAxisLeft().setAxisMinimum(0.f);
chart.getAxisLeft().setAxisMaximum(1.f); chart.getAxisLeft().setAxisMaximum(1.f);
@@ -221,11 +213,19 @@ public class textType extends inputType {
chart.getAxisRight().setAxisMaximum(1.f); chart.getAxisRight().setAxisMaximum(1.f);
Legend legend = chart.getLegend(); Legend legend = chart.getLegend();
legend.setTextColor(Color.WHITE); legend.setTextColor(chart_text);
chart.invalidate(); chart.invalidate();
parent.addView(chart); parent.addView(chart);
} }
public void addDataToTable(TableLayout parent, Map<Integer, List<RawDataType>> data){
}
public String toString(RawDataType data){
return String.valueOf(data.get());
}
} }
@@ -1,230 +0,0 @@
package com.ridgebotics.ridgescout.types.input;
import static android.text.InputType.TYPE_CLASS_NUMBER;
import android.content.Context;
import android.graphics.Color;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.components.Legend;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.data.LineDataSet;
import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.types.data.dataType;
import com.ridgebotics.ridgescout.types.data.intArrType;
import com.ridgebotics.ridgescout.types.data.intType;
import com.ridgebotics.ridgescout.ui.scouting.FieldPosView;
import com.ridgebotics.ridgescout.ui.scouting.MultiFieldPosView;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
public class fieldposType extends inputType {
public int get_byte_id() {return fieldposType;}
public inputTypes getInputType(){return inputTypes.FIELDPOS;}
public dataType.valueTypes getValueType(){return dataType.valueTypes.NUM;}
public Object get_fallback_value(){return 0;}
public fieldposType(){}
public String get_type_name(){return "Field Pos";}
public fieldposType(String name, int[] default_value){
super(name);
this.default_value = default_value;
}
public byte[] encode() throws ByteBuilder.buildingException {
ByteBuilder bb = new ByteBuilder();
bb.addString(name);
bb.addIntArray((int[]) default_value);
return bb.build();
}
public void decode(byte[] bytes) throws BuiltByteParser.byteParsingExeption {
BuiltByteParser bbp = new BuiltByteParser(bytes);
ArrayList<BuiltByteParser.parsedObject> objects = bbp.parse();
name = (String) objects.get(0).get();
default_value = objects.get(1).get();
System.out.println("Defalt value!!!!!" + default_value);
}
public FieldPosView field = null;
public View createView(Context context, Function<dataType, Integer> onUpdate){
field = new FieldPosView(context, pos -> {
onUpdate.apply(new intArrType(name, pos));
});
setViewValue(default_value);
return field;
}
public void setViewValue(Object value) {
if(field == null) return;
if(intArrType.isNull((int[]) value)){
nullify();
return;
}
isBlank = false;
field.setVisibility(View.VISIBLE);
field.setPos((int[]) value);
}
public void nullify(){
isBlank = true;
field.setVisibility(View.GONE);
}
public dataType getViewValue(){
if(field == null) return null;
if(field.getVisibility() == View.GONE) return intArrType.newNull(name);
return new intArrType(name, field.getPos());
}
public void add_individual_view(LinearLayout parent, dataType data){
if(data.isNull()) return;
FieldPosView fp = new FieldPosView(parent.getContext());
fp.setEnabled(false);
fp.setPos((int[]) data.get());
parent.addView(fp);
}
private static float calculateMean(int[] data) {
float sum = 0;
for (int value : data) {
sum += (float) value;
}
return sum / data.length;
}
private static float calculateStandardDeviation(int[] data, float mean) {
float sum = 0;
for (int value : data) {
sum += (float) Math.pow((float) value - mean, 2);
}
return (float) Math.sqrt(sum / (data.length - 1));
}
private static List<Entry> generateNormalDistribution(float mean, float stdDev, int count, int scale) {
List<Entry> entries = new ArrayList<>();
for (int i = 0; i < count; i++) {
float y = (float) ((1 / (stdDev * Math.sqrt(2 * Math.PI)))
* Math.exp(-0.5 * Math.pow(((float) i - mean) / stdDev, 2)));
entries.add(new Entry((float) i, y*scale)); // Scale y for visibility
}
return entries;
}
private static int findMin(dataType[] data){
int min = (int)data[0].get();
for(int i = 1; i < data.length; i++)
if((int)data[i].get() < min)
min = (int)data[i].get();
return min;
}
private static int findMax(dataType[] data){
int max = (int)data[0].get();
for(int i = 1; i < data.length; i++)
if((int)data[i].get() > max)
max = (int)data[i].get();
return max;
}
public void add_compiled_view(LinearLayout parent, dataType[] data){
MultiFieldPosView mfp = new MultiFieldPosView(parent.getContext());
for(int i = 0; i < data.length; i++){
if(data[i].isNull()) continue;
mfp.addPos((int[]) data[i].get());
}
parent.addView(mfp);
}
public void add_history_view(LinearLayout parent, dataType[] data){
LineChart chart = new LineChart(parent.getContext());
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
layout.height = 350;
chart.setLayoutParams(layout);
chart.setBackgroundColor(0xff252025);
int min = 0;
int max = 255;
List<Entry> entries = new ArrayList<>();
for (int i = 0; i < data.length; i++){
if(data[i] == null) continue;
if(data[i].isNull()) continue;
entries.add(new Entry(i, 255-(float)((int[]) data[i].get())[1]));
}
LineDataSet dataSet = new LineDataSet(entries, "Field position Y value");
dataSet.setColor(Color.BLUE);
dataSet.setValueTextColor(Color.BLACK);
dataSet.setDrawCircles(false);
dataSet.setDrawValues(false);
LineData lineData = new LineData(dataSet);
chart.setData(lineData);
chart.invalidate();
chart.getDescription().setEnabled(false);
chart.setTouchEnabled(false);
chart.setDragEnabled(false);
chart.setScaleEnabled(false);
dataSet.setValueTextColor(Color.RED);
chart.getXAxis().setTextColor(Color.WHITE);
chart.getAxisLeft().setTextColor(Color.WHITE);
chart.getAxisRight().setTextColor(Color.WHITE);
Legend legend = chart.getLegend();
legend.setTextColor(Color.WHITE);
chart.getAxisLeft().setAxisMinimum(min);
chart.getAxisLeft().setAxisMaximum(max);
chart.getAxisRight().setAxisMinimum(min);
chart.getAxisRight().setAxisMaximum(max);
parent.addView(chart);
}
}
@@ -1,107 +0,0 @@
package com.ridgebotics.ridgescout.types.input;
import android.content.Context;
import android.view.View;
import android.widget.LinearLayout;
import com.ridgebotics.ridgescout.types.data.dataType;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder;
import java.util.function.Function;
public abstract class inputType {
public static final int slider_type_id = 255;
public static final int dropdownType = 254;
public static final int notesType = 253;
public static final int tallyType = 252;
public static final int numberType = 251;
public static final int checkboxType = 250;
public static final int fieldposType = 249;
public enum inputTypes {
SLIDER,
DROPDOWN,
NOTES_INPUT,
TALLY,
NUMBER,
CHECKBOX,
FIELDPOS
}
public String name;
public Object default_value;
public abstract inputTypes getInputType();
public abstract dataType.valueTypes getValueType();
public abstract Object get_fallback_value();
public abstract int get_byte_id();
public inputType(){}
public inputType(String name){
this.name = name;
}
public abstract String get_type_name();
public abstract byte[] encode() throws ByteBuilder.buildingException;
public abstract void decode(byte[] bytes) throws BuiltByteParser.byteParsingExeption;
// public abstract dataType[] getConfig();
// public abstract void setConfig(dataType[] config);
public abstract View createView(Context context, Function<dataType, Integer> onUpdate);
public boolean isBlank = false;
public abstract void nullify();
public void setViewValue(dataType type){setViewValue(type.get());}
public abstract void setViewValue(Object value);
public abstract dataType getViewValue();
// private enum parameterTypeEnum {
// paramNumber,
// paramString,
// paramStringArray
// }
//
// public static class parameterType {
// public String name;
// public parameterTypeEnum id;
// }
//
// public static class paramNumber extends parameterType {
// public int val;
// public paramNumber(String name, int val){
// this.name = name + " (Number)";
// this.val = val;
// this.id = parameterTypeEnum.paramNumber;
// }
// }
//
// public static class paramString extends parameterType {
// public String val;
// public paramString(String name, String val){
// this.name = name + " (String)";
// this.val = val;
// this.id = parameterTypeEnum.paramString;
// }
// }
//
// public static class paramStringArray extends parameterType {
// public String[] val;
// public paramStringArray(String name, String[] val){
// this.name = name + " (String array)";
// this.val = val;
// this.id = parameterTypeEnum.paramStringArray;
// }
// }
// public abstract parameterType[] getDefaultParameters();
public abstract void add_individual_view(LinearLayout parent, dataType data);
public abstract void add_compiled_view(LinearLayout parent, dataType[] data);
public abstract void add_history_view(LinearLayout parent, dataType[] data);
}
@@ -1,126 +0,0 @@
package com.ridgebotics.ridgescout.ui;
import static com.ridgebotics.ridgescout.utility.DataManager.evcode;
import static com.ridgebotics.ridgescout.utility.DataManager.event;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.databinding.FragmentTeamSelectorBinding;
import com.ridgebotics.ridgescout.types.frcTeam;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.DataManager;
import com.ridgebotics.ridgescout.utility.fileEditor;
import java.util.Arrays;
public class TeamSelectorFragment extends Fragment {
private FragmentTeamSelectorBinding binding;
private static boolean pits_mode;
public static void setPits_mode(boolean mode){
pits_mode = mode;
}
private static onTeamSelected onSelect = new onTeamSelected() {@Override public void onSelect(TeamSelectorFragment self, frcTeam team) {}};
public interface onTeamSelected {
void onSelect(TeamSelectorFragment self, frcTeam team);
}
public static void setOnSelect(onTeamSelected tmponSelect){
onSelect = tmponSelect;
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentTeamSelectorBinding.inflate(inflater, container, false);
// event = fileEditor.g
DataManager.reload_event();
if(evcode == null || evcode.equals("unset")){
AlertManager.error("You somehow have not loaded an event!");
return binding.getRoot();
}
load_teams();
return binding.getRoot();
}
public void load_teams(){
// binding.pitFileIndicator.setVisibility(View.GONE);
// binding.pitTeamName.setVisibility(View.GONE);
// binding.pitTeamDescription.setVisibility(View.GONE);
//
// clear_fields();
int[] teamNums = new int[event.teams.size()];
for(int i = 0 ; i < event.teams.size(); i++){
teamNums[i] = event.teams.get(i).teamNumber;
}
Arrays.sort(teamNums);
TableLayout table = new TableLayout(getContext());
table.setStretchAllColumns(true);
binding.teams.addView(table);
for(int i = 0; i < event.teams.size(); i++){
frcTeam team = null;
for(int a = 0 ; a < event.teams.size(); a++){
if(event.teams.get(a).teamNumber == teamNums[i]){
team = event.teams.get(a);
break;
}
}
TableRow tr = new TableRow(getContext());
TableLayout.LayoutParams rowParams = new TableLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT
);
rowParams.setMargins(20,20,20,20);
tr.setLayoutParams(rowParams);
tr.setPadding(20,20,20,20);
table.addView(tr);
if(!pits_mode || fileEditor.fileExist(evcode + "-" + team.teamNumber + ".pitscoutdata")){
tr.setBackgroundColor(0x3000FF00);
}else{
tr.setBackgroundColor(0x30FF0000);
}
TextView tv = new TextView(getContext());
tv.setText(String.valueOf(team.teamNumber));
tv.setTextSize(20);
tr.addView(tv);
tv = new TextView(getContext());
tv.setText(team.teamName);
tv.setTextSize(16);
tr.addView(tv);
frcTeam finalTeam = team;
tr.setOnClickListener(v -> {
onSelect.onSelect(this, finalTeam);
});
}
}
}
@@ -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,7 +1,14 @@
package com.ridgebotics.ridgescout.ui.data; package com.ridgebotics.ridgescout.ui.data;
import static androidx.navigation.fragment.FragmentKt.findNavController; import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static com.ridgebotics.ridgescout.utility.Colors.datafragment_option_1;
import static com.ridgebotics.ridgescout.utility.Colors.datafragment_option_2;
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 android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@@ -13,67 +20,119 @@ 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.utility.settingsManager;
import com.ridgebotics.ridgescout.databinding.FragmentDataBinding;
import com.ridgebotics.ridgescout.types.frcTeam; import com.ridgebotics.ridgescout.types.frcTeam;
import com.ridgebotics.ridgescout.ui.TeamSelectorFragment; import com.ridgebotics.ridgescout.ui.views.FieldBorderedRow;
import com.ridgebotics.ridgescout.utility.fileEditor; import com.ridgebotics.ridgescout.ui.views.TeamListOption;
import com.ridgebotics.ridgescout.types.frcEvent; import com.ridgebotics.ridgescout.utility.DataManager;
import com.ridgebotics.ridgescout.utility.SettingsManager;
import com.ridgebotics.ridgescout.databinding.FragmentDataBinding;
import java.util.Arrays;
import java.util.List;
// Fragment for the menu of the data tab.
// Shows either by field type or by team.
// This is held in a sub-fragment under DataParentFragment
public class DataFragment extends Fragment { public class DataFragment extends Fragment {
private FragmentDataBinding binding; private FragmentDataBinding binding;
private int option = 0;
private boolean submenu = false;
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 = FragmentDataBinding.inflate(inflater, container, false); binding = FragmentDataBinding.inflate(inflater, container, false);
binding.table.setStretchAllColumns(true);
View root = binding.getRoot(); View root = binding.getRoot();
String evcode = settingsManager.getEVCode(); if(evcode == null || evcode.equals("unset") || event == null){
binding.noEventError.setVisibility(VISIBLE);
binding.dataTypeDropdown.setVisibility(GONE);
return root;
}
binding.fieldsButton.setOnClickListener(v -> { option = SettingsManager.getDataMode();
findNavController(this).navigate(R.id.action_navigation_data_to_navigation_data_fields_chooser);
binding.dataTypeDropdown.setTitle("Data type");
binding.dataTypeDropdown.setOptions(List.of(new String[]{
"By Team", "By Data Field"
}), option);
binding.dataTypeDropdown.setOnClickListener((item, index) -> {
option = index;
SettingsManager.setDataMode(option);
reload_views();
}); });
if(evcode.equals("unset")){ reload_views();
binding.noEventError.setVisibility(View.VISIBLE);
binding.buttons.setVisibility(View.VISIBLE);
binding.teamsButton.setEnabled(false);
binding.compareButton.setEnabled(false);
binding.reportButton.setEnabled(false);
binding.fieldsButton.setVisibility(View.VISIBLE);
return root; return root;
} }
frcEvent event = frcEvent.decode(fileEditor.readFile(evcode + ".eventdata")); public void reload_views(){
binding.table.removeViews(1, binding.table.getChildCount()-1);
binding.teamsButton.setOnClickListener(v -> { switch (option) {
TeamSelectorFragment.setPits_mode(false); case 0:
TeamSelectorFragment.setOnSelect(new TeamSelectorFragment.onTeamSelected() { load_teams();
@Override break;
public void onSelect(TeamSelectorFragment self, frcTeam team) { case 1:
TeamsFragment.setTeam(team); load_fields();
findNavController(self).navigate(R.id.action_navigation_team_selector_to_navigation_data_teams); break;
} }
});
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 -> { public void load_teams(){
findNavController(this).navigate(R.id.action_navigation_data_to_navigation_data_report_selector);
});
return root; int[] teamNums = new int[event.teams.size()];
for(int i = 0 ; i < event.teams.size(); i++){
teamNums[i] = event.teams.get(i).teamNumber;
}
Arrays.sort(teamNums);
for(int i = 0; i < event.teams.size(); i++){
frcTeam team = null;
for(int a = 0 ; a < event.teams.size(); a++){
if(event.teams.get(a).teamNumber == teamNums[i]){
team = event.teams.get(a);
break;
}
}
assert team != null;
TeamListOption teamRow = new TeamListOption(getContext());
binding.table.addView(teamRow);
teamRow.fromTeam(team);
frcTeam finalTeam = team;
teamRow.setOnClickListener(v -> {
TeamsFragment.setTeam(finalTeam);
((DataParentFragment) getParentFragment()).moveToFragment(new TeamsFragment());
// findNavController(this).navigate(R.id.action_navigation_data_parent_to_navigation_data_teams);
});
}
}
public void load_fields(){
DataManager.reload_match_fields();
if(match_latest_values == null) return;
for(int i = 0; i < match_latest_values.length; i++){
FieldBorderedRow tr = new FieldBorderedRow(getContext());
tr.fromField(match_latest_values[i]);
tr.setColor(i % 2 == 0 ? datafragment_option_1 : datafragment_option_2);
binding.table.addView(tr);
final int fi = i;
tr.setOnClickListener(v -> {
FieldDataFragment.setFieldIndex(fi);
((DataParentFragment) getParentFragment()).moveToFragment(new FieldDataFragment());
// findNavController(get).navigate(R.id.action_navigation_data_parent_to_navigation_data_field_data);
});
}
} }
} }
@@ -0,0 +1,183 @@
package com.ridgebotics.ridgescout.ui.data;
import static android.content.Context.CLIPBOARD_SERVICE;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static com.ridgebotics.ridgescout.utility.DataManager.evcode;
import static com.ridgebotics.ridgescout.utility.DataManager.event;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.databinding.FragmentDataParentBinding;
import com.ridgebotics.ridgescout.types.frcMatch;
import com.ridgebotics.ridgescout.types.frcTeam;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.AutoSaveManager;
import com.ridgebotics.ridgescout.utility.DataManager;
import com.ridgebotics.ridgescout.utility.SettingsManager;
import java.util.ArrayList;
import java.util.List;
// Holds the scouting data editor aswell as the sub-fragment for the scouting data browser
public class DataParentFragment extends Fragment {
private FragmentDataParentBinding binding;
private DataFragment dataFragment;
private boolean editBoxEnabled = true;
private int teamNum = SettingsManager.getTeamNum();
private frcMatch[] ourMatches;
private int matchIndex = 0;
private AutoSaveManager asm;
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentDataParentBinding.inflate(inflater, container, false);
DataManager.reload_event();
if (savedInstanceState == null && dataFragment == null){
dataFragment = new DataFragment();
//add child fragment
getChildFragmentManager()
.beginTransaction()
.add(R.id.data_subfragment, dataFragment, "Data Subfragment")
.commit();
}
if(evcode.equals("unset") || event == null){
binding.reportToggleButton.setVisibility(GONE);
return binding.getRoot();
}
ourMatches = event.getTeamMatches(SettingsManager.getTeamNum());
matchIndex = SettingsManager.getReportMatchIndex(evcode);
if(ourMatches.length == 0){
binding.reportToggleButton.setVisibility(GONE);
return binding.getRoot();
}
binding.scoutUpButton.setOnClickListener(v -> {
matchIndex++;
updateButtons();
});
binding.scoutDownButton.setOnClickListener(v -> {
matchIndex--;
updateButtons();
});
updateButtons();
binding.reportToggleButton.setOnClickListener(view -> {
editBoxEnabled =! editBoxEnabled;
binding.ScoutingEditBox.setVisibility(editBoxEnabled ? GONE : VISIBLE);
binding.reportToggleButton.setText(editBoxEnabled ? "▲ report" : "▼ report");
});
binding.reportCopyButton.setOnClickListener(v -> {
// ClipData e = new ClipData();
ClipboardManager clipboardManager = (ClipboardManager) getContext().getSystemService(CLIPBOARD_SERVICE);
ClipData clipData = ClipData.newPlainText(
"Scouting report",
binding.scoutingReportEdittext.getText().toString()
);
clipboardManager.setPrimaryClip(clipData);
AlertManager.toast("Copied report to clipboard!");
});
asm = new AutoSaveManager(() -> SettingsManager.setScoutingReport(evcode, matchIndex, binding.scoutingReportEdittext.getText().toString()), 300);
asm.start();
binding.scoutingReportEdittext.addTextChangedListener(new TextWatcher() {
@Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
@Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
asm.update();
}
@Override public void afterTextChanged(Editable editable) {}
});
return binding.getRoot();
}
private void updateButtons(){
binding.matchNum.setText(String.valueOf(ourMatches[matchIndex].matchIndex));
binding.scoutUpButton.setEnabled(matchIndex < ourMatches.length-1);
binding.scoutDownButton.setEnabled(matchIndex > 0);
SettingsManager.setReportIndex(matchIndex, evcode);
String report = SettingsManager.getScoutingReport(evcode, matchIndex);
if(report.isEmpty()) report = generateScoutingTemplate(ourMatches[matchIndex]);
binding.scoutingReportEdittext.setText(report);
}
public void moveToFragment(Fragment newFragment){
// consider using Java coding conventions (upper first char class names!!!)
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack
transaction.replace(R.id.data_subfragment, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
}
// Generate format for scouting data
private String generateScoutingTemplate(frcMatch nextMatch){
boolean isBlueAlliance = event.getIsBlueAlliance(teamNum, nextMatch);
List<frcTeam> ourAlliance = new ArrayList<>();
List<frcTeam> opposingAlliance = new ArrayList<>();
for(int a = 0; a < nextMatch.blueAlliance.length; a++)
if(nextMatch.blueAlliance[a] != teamNum){
(isBlueAlliance ? ourAlliance : opposingAlliance).add(event.getTeamByNum(nextMatch.blueAlliance[a]));
}
for(int a = 0; a < nextMatch.redAlliance.length; a++)
if(nextMatch.redAlliance[a] != teamNum){
(!isBlueAlliance ? ourAlliance : opposingAlliance).add(event.getTeamByNum(nextMatch.redAlliance[a]));
}
String output = "Match: " + (nextMatch.matchIndex) + "\n";
output += "## Our Alliance ##";
output += getTeamNameAndNum(ourAlliance.get(0));
output += getTeamNameAndNum(ourAlliance.get(1));
output += "\n## Opposing Alliance ##";
output += getTeamNameAndNum(opposingAlliance.get(0));
output += getTeamNameAndNum(opposingAlliance.get(1));
output += getTeamNameAndNum(opposingAlliance.get(2));
return output;
}
private static String getTeamNameAndNum(frcTeam team){
return "\n" + team.teamNumber + " " + team.teamName + ": \n";
}
}
@@ -0,0 +1,108 @@
package com.ridgebotics.ridgescout.ui.data;
import com.ridgebotics.ridgescout.types.data.RawDataType;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
// Helper class for scouting data by fields.
public class DataProcessing {
public static int[] getNumberBounds(Map<Integer, List<RawDataType>> data){
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
for(Integer teamNum : data.keySet()){
List<RawDataType> teamData = data.get(teamNum);
int[] locBounds = getNumberBounds(teamData);
if(locBounds[1] > max) max = locBounds[1];
if(locBounds[0] < min) min = locBounds[0];
}
return new int[]{min, max};
}
public static int[] getNumberBounds(List<RawDataType> data){
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
if(data == null) return new int[]{min, max};
for(int i = 0; i < data.size(); i++){
RawDataType dataPoint = data.get(i);
// if(dataPoint == null) continue;
int num = (int) dataPoint.get();
if(num > max) max = num;
if(num < min) min = num;
}
return new int[]{min, max};
}
//https://stackoverflow.com/questions/42381759/finding-first-quartile-and-third-quartile-in-integer-array-using-java#63891545
public static float[] getQuartiles(List<RawDataType> data) {
float ans[] = new float[3];
float[] val = new float[data.size()];
for(int i = 0; i < val.length; i++){
val[i] = (int) data.get(i).get();
}
for (int quartileType = 1; quartileType < 4; quartileType++) {
float length = val.length + 1;
float quartile;
float newArraySize = (length * ((float) (quartileType) * 25 / 100)) - 1;
Arrays.sort(val);
if (newArraySize % 1 == 0) {
quartile = val[(int) (newArraySize)];
} else {
int newArraySize1 = (int) (newArraySize);
quartile = (val[newArraySize1] + val[newArraySize1 + 1]) / 2;
}
ans[quartileType - 1] = quartile;
}
return ans;
}
/**
* Calculates the specified percentile of a dataset.
*
* @param data The dataset to analyze
* @param percentile The percentile to find (0-100)
* @return The value at the specified percentile
*/
public static float calculatePercentile(float[] data, float percentile) {
// Make a copy of the data to avoid modifying the original
float[] dataCopy = Arrays.copyOf(data, data.length);
// Sort the data in ascending order
Arrays.sort(dataCopy);
// Calculate the position as a fraction
float position = (percentile / 100.0f) * (dataCopy.length - 1);
// Get the integer part of the position
int lowerIndex = (int) Math.floor(position);
int upperIndex = (int) Math.ceil(position);
// If position is already an integer, return the value at that position
if (lowerIndex == upperIndex) {
return dataCopy[lowerIndex];
}
// Otherwise, interpolate between the two adjacent values
float fraction = position - lowerIndex;
float lowerValue = dataCopy[lowerIndex];
float upperValue = dataCopy[upperIndex];
// Linear interpolation
return lowerValue + fraction * (upperValue - lowerValue);
}
}
@@ -0,0 +1,114 @@
package com.ridgebotics.ridgescout.ui.data;
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.match_transferValues;
import static com.ridgebotics.ridgescout.utility.DataManager.match_values;
import static com.ridgebotics.ridgescout.utility.DataManager.rescout_list;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.util.Log;
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.FragmentDataFieldDataBinding;
import com.ridgebotics.ridgescout.scoutingData.ScoutingDataWriter;
import com.ridgebotics.ridgescout.types.data.RawDataType;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.FileEditor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
// Fragment for viewing the data of a specfic field.
public class FieldDataFragment extends Fragment {
private FragmentDataFieldDataBinding binding;
private static int fieldIndex = -1;
public static void setFieldIndex(int index){
fieldIndex = index;
}
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentDataFieldDataBinding.inflate(inflater, container, false);
binding.table.setStretchAllColumns(true);
View root = binding.getRoot();
if (fieldIndex == -1)
return root;
startLoading("Loading data...");
Thread t = new Thread(() -> {
Map<Integer, List<RawDataType>> data = new HashMap<>();
for (int teamIndex = 0; teamIndex < event.teams.size(); teamIndex++) {
int teamNum = event.teams.get(teamIndex).teamNumber;
List<String> filenames = new ArrayList<>(List.of(FileEditor.getMatchesByTeamNum(evcode, event.teams.get(teamIndex).teamNumber)));
filenames.removeAll(rescout_list.get());
ArrayList<RawDataType> teamData = new ArrayList<>();
for (int i = 0; i < filenames.size(); i++) {
try {
Log.i(getClass().toString(), "Loading: " + filenames.get(i));
ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(filenames.get(i), match_values, match_transferValues);
if (psda.data.array[fieldIndex] != null && psda.data.array[fieldIndex].get() != null && !psda.data.array[fieldIndex].isNull())
teamData.add(psda.data.array[fieldIndex]);
} catch (Exception e) {
AlertManager.error("Failure to load file " + filenames.get(i), e);
}
}
data.put(teamNum, teamData);
}
Log.i(getClass().toString(), "Finished!");
getActivity().runOnUiThread(() -> {
binding.table.setStretchAllColumns(false);
match_latest_values[fieldIndex].addDataToTable(binding.table, data);
stopLoading();
});
});
t.start();
return root;
}
private ProgressDialog loadingDialog;
private void startLoading(String title){
getActivity().runOnUiThread(() -> {
if(loadingDialog != null && loadingDialog.isShowing())
loadingDialog.dismiss();
loadingDialog = ProgressDialog.show(getActivity(), title, "Please wait...");
});
}
private void stopLoading(){
getActivity().runOnUiThread(() -> {
if (loadingDialog != null)
loadingDialog.cancel();
loadingDialog = null;
});
}
}
@@ -1,322 +0,0 @@
package com.ridgebotics.ridgescout.ui.data;
import static android.text.InputType.TYPE_CLASS_NUMBER;
import android.content.Context;
import android.view.View;
import android.widget.EditText;
import android.widget.TableLayout;
import android.widget.TextView;
import com.ridgebotics.ridgescout.types.input.checkboxType;
import com.ridgebotics.ridgescout.types.input.dropdownType;
import com.ridgebotics.ridgescout.types.input.fieldposType;
import com.ridgebotics.ridgescout.types.input.inputType;
import com.ridgebotics.ridgescout.types.input.numberType;
import com.ridgebotics.ridgescout.types.input.sliderType;
import com.ridgebotics.ridgescout.types.input.tallyType;
import com.ridgebotics.ridgescout.types.input.textType;
import com.ridgebotics.ridgescout.utility.AlertManager;
public class FieldEditorHelper {
private enum parameterTypeEnum {
paramNumber,
paramString,
paramStringArray,
paramNumberArray
}
public static class parameterType {
public String name;
public parameterTypeEnum id;
}
public static class paramNumber extends parameterType{
public int val;
public paramNumber(String name, int val){
this.name = name + " (Number)";
this.val = val;
this.id = parameterTypeEnum.paramNumber;
}
}
public static class paramString extends parameterType {
public String val;
public paramString(String name, String val){
this.name = name + " (String)";
this.val = val;
this.id = parameterTypeEnum.paramString;
}
}
public static class paramStringArray extends parameterType{
public String[] val;
public paramStringArray(String name, String[] val){
this.name = name + " (String array)";
this.val = val;
this.id = parameterTypeEnum.paramStringArray;
}
}
// public static class paramNumberArray extends parameterType{
// public int[] val;
// public paramNumberArray(String name, int[] val){
// this.name = name + " (Number array)";
// this.val = val;
// this.id = parameterTypeEnum.paramNumberArray;
// }
// }
public static final parameterType[] defaultSliderParams = new parameterType[]{
new paramNumber("Min", 0),
new paramNumber("Max", 10),
new paramNumber("Default Value", 5)
};
public static final parameterType[] defaultDropdownParams = new parameterType[]{
new paramStringArray("Default Value", new String[]{"Zero","One","Two","Three"}),
new paramNumber("Default Option", 0),
};
public static final parameterType[] defaultTextParams = new parameterType[]{
new paramString("Default Value", "")
};
public static final parameterType[] defaultTallyParams = new parameterType[]{
new paramNumber("Default Value", 0)
};
public static final parameterType[] defaultNumberParams = new parameterType[]{
new paramNumber("Default Value", 0)
};
public static final parameterType[] defaultCheckboxParam = new parameterType[]{
new paramNumber("Default Value ( 1 or 0 )", 0)
};
public static final parameterType[] defaultFieldPosParam = new parameterType[]{
new paramNumber("Default X", 0),
new paramNumber("Default Y", 0)
};
private static parameterType[] getSliderParams(sliderType s){
return new parameterType[]{
new paramNumber("Min", s.min),
new paramNumber("Max", s.max),
new paramNumber("Default Value", (int) s.default_value)
};
}
private static parameterType[] getDropdownParams(dropdownType s){
return new parameterType[]{
new paramStringArray("Default Value",s.text_options),
new paramNumber("Default Option", (int) s.default_value),
};
}
private static parameterType[] getTextParams(textType s){
return new parameterType[]{
new paramString("Default Value", (String) s.default_value)
};
}
private static parameterType[] getTallyParams(tallyType s){
return new parameterType[]{
new paramNumber("Default Value", (int) s.default_value)
};
}
private static parameterType[] getNumberParams(numberType s){
return new parameterType[]{
new paramNumber("Default Value", (int) s.default_value)
};
}
private static parameterType[] getCheckboxParam(checkboxType s){
return new parameterType[]{
new paramNumber("Default Value ( 1 or 0 )", (int) s.default_value)
};
}
private static parameterType[] getFieldPosParam(fieldposType s){
return new parameterType[]{
new paramNumber("Default X", ((int[]) s.default_value)[0]),
new paramNumber("Default Y", ((int[]) s.default_value)[1])
};
}
public static void setSliderParams(sliderType s, parameterType[] types){
s.min = ((paramNumber) types[0]).val;
s.max = ((paramNumber) types[1]).val;
s.default_value = ((paramNumber) types[2]).val;
}
public static void setDropdownParams(dropdownType s, parameterType[] types){
s.text_options = ((paramStringArray) types[0]).val;
s.default_value = ((paramNumber) types[1]).val;
}
public static void setTextParams(textType s, parameterType[] types){
s.default_value = ((paramString) types[0]).val;
}
public static void setTallyParams(tallyType s, parameterType[] types){
s.default_value = ((paramNumber) types[0]).val;
}
public static void setNumberParams(numberType s, parameterType[] types){
s.default_value = ((paramNumber) types[0]).val;
}
public static void setCheckboxParam(checkboxType s, parameterType[] types){
s.default_value = ((paramNumber) types[0]).val;
}
public static void setFieldPosParam(fieldposType s, parameterType[] types){
s.default_value = new int[]{
((paramNumber) types[0]).val,
((paramNumber) types[1]).val
};
}
private static void setInputParameter(inputType t, parameterType[] types){
switch (t.getInputType()){
case TALLY:
setTallyParams((tallyType) t, types);
break;
case SLIDER:
setSliderParams((sliderType) t, types);
break;
case DROPDOWN:
setDropdownParams((dropdownType) t, types);
break;
case NOTES_INPUT:
setTextParams((textType) t, types);
break;
case NUMBER:
setNumberParams((numberType) t, types);
break;
case CHECKBOX:
setCheckboxParam((checkboxType) t, types);
break;
case FIELDPOS:
setFieldPosParam((fieldposType) t, types);
break;
}
}
private static parameterType[] getParamsFromInputType(inputType t){
switch (t.getInputType()){
case TALLY:
return getTallyParams((tallyType) t);
case SLIDER:
return getSliderParams((sliderType) t);
case DROPDOWN:
return getDropdownParams((dropdownType) t);
case NOTES_INPUT:
return getTextParams((textType) t);
case NUMBER:
return getNumberParams((numberType) t);
case CHECKBOX:
return getCheckboxParam((checkboxType) t);
case FIELDPOS:
return getFieldPosParam((fieldposType) t);
}
return new parameterType[]{};
}
private static View createNumberEdit(Context c, int value){
EditText text = new EditText(c);
text.setInputType(TYPE_CLASS_NUMBER);
text.setText(String.valueOf(value));
return text;
}
private static View createStringEdit(Context c, String value){
EditText text = new EditText(c);
text.setText(value);
return text;
}
private static View createStringArrayEdit(Context c, String[] value){
EditText text = new EditText(c);
text.setText(String.join("\n", value));
return text;
}
private static View createEdit(Context c, parameterType t){
switch (t.id){
case paramNumber:
return createNumberEdit(c, ((paramNumber) t).val);
case paramString:
return createStringEdit(c, ((paramString) t).val);
case paramStringArray:
return createStringArrayEdit(c, ((paramStringArray) t).val);
}
return null;
}
private static boolean readEdit(View v, parameterType t){
try{
String val;
switch (t.id) {
case paramNumber:
val = ((EditText) v).getText().toString();
if(val.isEmpty() || val.isBlank()) return false;
((paramNumber) t).val = Integer.parseInt(val);
break;
case paramString:
val = ((EditText) v).getText().toString();
//if(val.isEmpty() || val.isBlank()) return false;
((paramString) t).val = val;
break;
case paramStringArray:
val = ((EditText) v).getText().toString();
if(val.isEmpty() || val.isBlank()) return false;
((paramStringArray) t).val = val.split("\n");
break;
}
} catch (Exception e) {
AlertManager.error(e);
return false;
}
return true;
}
private parameterType[] types;
private View[] views;
private inputType t;
public FieldEditorHelper(Context c, inputType t, TableLayout parentView, parameterType[] tmptypes){
this.types = tmptypes;
this.t = t;
views = new View[types.length];
for(int i = 0; i < types.length; i++){
TextView tv = new TextView(c);
tv.setText(types[i].name);
tv.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
tv.setTextSize(20);
parentView.addView(tv);
views[i] = createEdit(c, types[i]);
parentView.addView(views[i]);
}
}
public FieldEditorHelper(Context c, inputType t, TableLayout parentView){
this(c,t,parentView,getParamsFromInputType(t));
}
public boolean save(){
for(int i = 0; i < types.length; i++){
if(!readEdit(views[i], types[i]))
return false;
}
setInputParameter(t, types);
return true;
}
}
@@ -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();
}
}
@@ -1,546 +0,0 @@
package com.ridgebotics.ridgescout.ui.data;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.text.InputType;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation;
import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.databinding.FragmentDataFieldsBinding;
import com.ridgebotics.ridgescout.scoutingData.fields;
import com.ridgebotics.ridgescout.types.input.checkboxType;
import com.ridgebotics.ridgescout.types.input.dropdownType;
import com.ridgebotics.ridgescout.types.input.fieldposType;
import com.ridgebotics.ridgescout.types.input.inputType;
import com.ridgebotics.ridgescout.types.input.numberType;
import com.ridgebotics.ridgescout.types.input.sliderType;
import com.ridgebotics.ridgescout.types.input.tallyType;
import com.ridgebotics.ridgescout.types.input.textType;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.skydoves.powerspinner.IconSpinnerAdapter;
import com.skydoves.powerspinner.IconSpinnerItem;
import com.skydoves.powerspinner.PowerSpinnerView;
import com.skydoves.powerspinner.SpinnerGravity;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class FieldsFragment extends Fragment {
FragmentDataFieldsBinding binding;
private static String filename;
public static void set_filename(String tmpfilename){
filename = tmpfilename;
}
@SuppressLint("ClickableViewAccessibility")
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentDataFieldsBinding.inflate(inflater, container, false);
binding.revertVersionButton.setVisibility(View.VISIBLE);
binding.valueEditScrollview.setOnTouchListener((v, event) -> true);
binding.saveButton.setVisibility(View.GONE);
binding.cancelEditButton.setVisibility(View.GONE);
binding.editButton.setVisibility(View.GONE);
binding.upButton.setVisibility(View.GONE);
binding.addButton.setVisibility(View.GONE);
binding.downButton.setVisibility(View.GONE);
binding.deleteButton.setVisibility(View.GONE);
binding.valueEditContainer.setVisibility(View.GONE);
load_field_menu();
return binding.getRoot();
}
private static final int background_color = 0x5000ff00;
private static final int unfocused_background_color = 0x2000ff00;
inputType[][] values;
private void load_field_menu() {
values = fields.load(filename);
binding.fieldsArea.bringToFront();
binding.fieldsArea.setStretchAllColumns(true);
binding.fieldsArea.removeAllViews();
binding.fieldsArea.setReorderingEnabled(false);
if(values == null) return;
for(int i = 0; i < values.length; i++){
TableRow tr = new TableRow(getContext());
TableLayout.LayoutParams rowParams = new TableLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT
);
rowParams.setMargins(20,20,20,20);
tr.setLayoutParams(rowParams);
tr.setPadding(20,20,20,20);
TextView tv = new TextView(getContext());
tv.setText("v" + i);
tv.setTextSize(20);
tr.addView(tv);
tv = new TextView(getContext());
tv.setText(values[i].length + " Fields");
tv.setTextSize(16);
tr.addView(tv);
binding.fieldsArea.addView(tr);
if(i == values.length-1) {
tr.setBackgroundColor(background_color);
int fi = i;
tr.setOnClickListener(v -> {
display_fields(values[fi]);
});
}else{
tr.setBackgroundColor(unfocused_background_color);
}
}
}
private void display_fields(inputType[] version_values) {
binding.fieldsArea.removeAllViews();
binding.fieldsArea.setReorderingEnabled(false);
binding.revertVersionButton.setVisibility(View.GONE);
binding.saveButton.setVisibility(View.GONE);
binding.editButton.setVisibility(View.GONE);
binding.upButton.setVisibility(View.VISIBLE);
binding.addButton.setVisibility(View.VISIBLE);
binding.downButton.setVisibility(View.VISIBLE);
binding.valueEditContainer.setVisibility(View.GONE);
for(int i = 0; i < version_values.length; i++){
addRow(version_values[i]);
}
selindex = -1;
binding.addButton.setOnClickListener(this::addField);
binding.saveButton.setOnClickListener(this::buttonfunc);
}
private void addRow(inputType field){
TableRow tr = getTableRow(field);
binding.fieldsArea.addView(tr);
tr.setOnClickListener(v -> {
binding.editButton.setVisibility(View.VISIBLE);
trOnClick(values[values.length-1], tr);
});
binding.upButton.setOnClickListener(v -> {
if(selindex != 0) {
binding.saveButton.setVisibility(View.VISIBLE);
binding.fieldsArea.updateRowOrder(selindex, selindex - 1);
selindex -= 1;
}
});
binding.downButton.setOnClickListener(v -> {
if(selindex != values[values.length-1].length-1) {
binding.saveButton.setVisibility(View.VISIBLE);
binding.fieldsArea.updateRowOrder(selindex, selindex + 1);
selindex += 1;
}
});
}
private void buttonfunc(View v){
AlertDialog.Builder alert = new AlertDialog.Builder(getContext());
alert.setTitle("Warning!");
alert.setMessage("Changing or removing some values will result in lost data!\nBut this will create a new field version, and you can revert at any time.");
alert.setPositiveButton("OK", null);
alert.setNegativeButton("Cancel", null);
alert.setCancelable(true);
alert.setOnDismissListener(b -> {
inputType[][] currentValues = fields.load(filename);
assert currentValues != null;
inputType[][] newValues = new inputType[currentValues.length+1][];
System.arraycopy(currentValues, 0, newValues, 0, currentValues.length);
newValues[newValues.length-1] = new inputType[values[values.length-1].length];
for(int i = 0; i < values[values.length-1].length; i++){
newValues[newValues.length-1][i] = values[values.length-1][binding.fieldsArea.getReorderedIndexes().get(i)];
}
// newValues[newValues.length-1] = values[values.length-1];
boolean saved = fields.save(filename, newValues);
AlertManager.alert("Saved", String.valueOf(saved));
Navigation.findNavController((Activity) getContext(), R.id.nav_host_fragment_activity_main).navigate(R.id.action_navigation_data_fields_to_navigation_data_fields_chooser);
});
alert.create().show();
}
private int selindex = -1;
private void trOnClick(inputType[] version_values, TableRow tr){
selindex = -1;
for(int i = 0; i < binding.fieldsArea.getChildCount(); i++){
View v = binding.fieldsArea.getChildAt(i);
if(v.equals(tr)) {
tr.setBackgroundColor(background_color);
selindex = i;
} else
binding.fieldsArea.getChildAt(i).setBackgroundColor(unfocused_background_color);
}
onFieldSelect(version_values[binding.fieldsArea.getReorderedIndexes().get(selindex)]);
}
private void onFieldSelect(inputType field){
//System.out.println(field.name);
binding.editButton.setOnClickListener(v -> {
binding.editButton.setVisibility(View.GONE);
binding.addButton.setVisibility(View.GONE);
binding.upButton.setVisibility(View.GONE);
binding.downButton.setVisibility(View.GONE);
binding.ValueEditTable.removeAllViews();
binding.valueEditContainer.setVisibility(View.VISIBLE);
TextView tv = new TextView(getContext());
tv.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
tv.setText(field.name);
tv.setPadding(8,8,8,8);
tv.setTextSize(24);
binding.ValueEditTable.addView(tv);
final FieldEditorHelper fe = new FieldEditorHelper(
getContext(),
field,
binding.ValueEditTable
);
binding.saveButton.setVisibility(View.VISIBLE);
binding.saveButton.setOnClickListener(a -> {
System.out.println(fe.save());
defaultVisibility();
});
binding.cancelEditButton.setVisibility(View.VISIBLE);
binding.cancelEditButton.setOnClickListener(a -> {
defaultVisibility();
});
binding.deleteButton.setVisibility(View.VISIBLE);
binding.deleteButton.setOnClickListener(a -> {
deleteField(field);
});
});
}
private void deleteField(inputType field){
AlertDialog.Builder alert = new AlertDialog.Builder(getContext());
alert.setTitle("Warning!");
alert.setMessage("Removing a value will result in data being deleted in subsequent field versions!");
alert.setNegativeButton("Cancel", null);
alert.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
defaultVisibility();
int oldindex = -1;
for(int i = 0; i < values[values.length - 1].length; i++){
if(values[values.length - 1][i].equals(field)){
oldindex = i;
break;
}
}
if(oldindex != -1) {
System.out.println(Arrays.toString(values[values.length - 1]));
binding.fieldsArea.removeViewAt(selindex);
binding.fieldsArea.removeElement(oldindex);
values[values.length - 1] = removeElement(values[values.length - 1], oldindex);
selindex = -1;
AlertManager.toast("Removed!");
binding.editButton.setVisibility(View.GONE);
System.out.println(Arrays.toString(values[values.length - 1]));
//System.out.println(values[values.length-1].length);
}
}
});
alert.setCancelable(true);
alert.create().show();
}
public inputType[] removeElement(inputType[] src, int i) {
inputType[] newArray = new inputType[src.length - 1];
if (i > 0){
System.arraycopy(src, 0, newArray, 0, i);
}
if (newArray.length > i){
System.arraycopy(src, i + 1, newArray, i, newArray.length - i);
}
return newArray;
}
private void defaultVisibility() {
binding.editButton.setVisibility(View.VISIBLE);
binding.addButton.setVisibility(View.VISIBLE);
binding.upButton.setVisibility(View.VISIBLE);
binding.downButton.setVisibility(View.VISIBLE);
binding.ValueEditTable.removeAllViews();
binding.valueEditContainer.setVisibility(View.GONE);
binding.cancelEditButton.setVisibility(View.GONE);
binding.deleteButton.setVisibility(View.GONE);
binding.saveButton.setOnClickListener(this::buttonfunc);
}
private void addField(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle("Title");
final EditText input = new EditText(getContext());
input.setInputType(InputType.TYPE_CLASS_TEXT);
builder.setView(input);
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String title = input.getText().toString();
if(title.isEmpty() || title.isBlank()) {
AlertManager.error("Title cannot be blank!");
return;
}
addField_Part_2(title);
}
});
builder.show();
}
private void addField_Part_2(String title) {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle("Title");
final PowerSpinnerView dropdown = new PowerSpinnerView(getContext());
List<IconSpinnerItem> iconSpinnerItems = new ArrayList<>();
iconSpinnerItems.add(new IconSpinnerItem("Slider"));
iconSpinnerItems.add(new IconSpinnerItem("Text"));
iconSpinnerItems.add(new IconSpinnerItem("Dropdown"));
iconSpinnerItems.add(new IconSpinnerItem("Tally"));
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.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
addField_Part_3(title, dropdown.getSelectedIndex());
}
});
builder.show();
}
private void addField_Part_3(String title, int typeIndex) {
switch (typeIndex){
case 0:
sliderType slider = new sliderType();
slider.name = title;
FieldEditorHelper.setSliderParams(slider, FieldEditorHelper.defaultSliderParams);
addField_Part_4(slider);
break;
case 1:
textType text = new textType();
text.name = title;
FieldEditorHelper.setTextParams(text, FieldEditorHelper.defaultTextParams);
addField_Part_4(text);
break;
case 2:
dropdownType dropdown = new dropdownType();
dropdown.name = title;
FieldEditorHelper.setDropdownParams(dropdown, FieldEditorHelper.defaultDropdownParams);
addField_Part_4(dropdown);
break;
case 3:
tallyType tally = new tallyType();
tally.name = title;
FieldEditorHelper.setTallyParams(tally, FieldEditorHelper.defaultTallyParams);
addField_Part_4(tally);
break;
case 4:
numberType num = new numberType();
num.name = title;
FieldEditorHelper.setNumberParams(num, FieldEditorHelper.defaultNumberParams);
addField_Part_4(num);
break;
case 5:
checkboxType cb = new checkboxType();
cb.name = title;
FieldEditorHelper.setCheckboxParam(cb, FieldEditorHelper.defaultCheckboxParam);
addField_Part_4(cb);
break;
case 6:
fieldposType fp = new fieldposType();
fp.name = title;
FieldEditorHelper.setFieldPosParam(fp, FieldEditorHelper.defaultFieldPosParam);
addField_Part_4(fp);
break;
}
}
private void addField_Part_4(inputType field) {
binding.editButton.setVisibility(View.GONE);
binding.addButton.setVisibility(View.GONE);
binding.upButton.setVisibility(View.GONE);
binding.downButton.setVisibility(View.GONE);
binding.ValueEditTable.removeAllViews();
binding.valueEditContainer.setVisibility(View.VISIBLE);
TextView tv = new TextView(getContext());
tv.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
tv.setText(field.name);
tv.setPadding(8,8,8,8);
tv.setTextSize(24);
binding.ValueEditTable.addView(tv);
final FieldEditorHelper fe = new FieldEditorHelper(
getContext(),
field,
binding.ValueEditTable
);
binding.saveButton.setVisibility(View.VISIBLE);
binding.saveButton.setOnClickListener(a -> {
inputType[] newValues = new inputType[values[values.length-1].length+1];
System.arraycopy(values[values.length-1], 0, newValues, 0, values[values.length-1].length);
newValues[newValues.length-1] = field;
values[values.length-1] = newValues;
AlertManager.alert("Test", String.valueOf(binding.fieldsArea.getReorderedIndexes()));
//TableRow tr = getTableRow(field);
//binding.fieldsArea.addView(tr);
addRow(field);
System.out.println(fe.save());
defaultVisibility();
});
binding.cancelEditButton.setVisibility(View.VISIBLE);
binding.cancelEditButton.setOnClickListener(a -> {
defaultVisibility();
});
}
private @NonNull TableRow getTableRow(inputType field) {
TableRow tr = new TableRow(getContext());
TableLayout.LayoutParams rowParams = new TableLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT
);
rowParams.setMargins(20,20,20,20);
tr.setLayoutParams(rowParams);
tr.setPadding(20,20,20,20);
tr.setBackgroundColor(unfocused_background_color);
TextView tv = new TextView(getContext());
tv.setText(field.get_type_name());
tv.setTextSize(12);
tr.addView(tv);
tv = new TextView(getContext());
tv.setText(field.name);
tv.setTextSize(20);
tr.addView(tv);
return tr;
}
}
@@ -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);
});
}
}
@@ -1,5 +1,7 @@
package com.ridgebotics.ridgescout.ui.data; package com.ridgebotics.ridgescout.ui.data;
import static com.ridgebotics.ridgescout.utility.Colors.toggletitle_black_background;
import static com.ridgebotics.ridgescout.utility.Colors.toggletitle_unselected;
import static com.ridgebotics.ridgescout.utility.DataManager.evcode; import static com.ridgebotics.ridgescout.utility.DataManager.evcode;
import static com.ridgebotics.ridgescout.utility.DataManager.match_latest_values; import static com.ridgebotics.ridgescout.utility.DataManager.match_latest_values;
import static com.ridgebotics.ridgescout.utility.DataManager.match_transferValues; import static com.ridgebotics.ridgescout.utility.DataManager.match_transferValues;
@@ -8,37 +10,35 @@ import static com.ridgebotics.ridgescout.utility.DataManager.pit_latest_values;
import static com.ridgebotics.ridgescout.utility.DataManager.pit_transferValues; import static com.ridgebotics.ridgescout.utility.DataManager.pit_transferValues;
import static com.ridgebotics.ridgescout.utility.DataManager.pit_values; import static com.ridgebotics.ridgescout.utility.DataManager.pit_values;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.view.Gravity; import android.view.Gravity;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TableLayout;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.utility.settingsManager; import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.SettingsManager;
import com.ridgebotics.ridgescout.databinding.FragmentDataTeamsBinding; import com.ridgebotics.ridgescout.databinding.FragmentDataTeamsBinding;
import com.ridgebotics.ridgescout.scoutingData.ScoutingDataWriter; import com.ridgebotics.ridgescout.scoutingData.ScoutingDataWriter;
import com.ridgebotics.ridgescout.types.data.dataType; import com.ridgebotics.ridgescout.types.data.RawDataType;
import com.ridgebotics.ridgescout.types.frcTeam; 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.ridgebotics.ridgescout.utility.builders.TextViewBuilder;
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;
// Fragment for viewing the data of a team.
public class TeamsFragment extends Fragment { public class TeamsFragment extends Fragment {
FragmentDataTeamsBinding binding; FragmentDataTeamsBinding binding;
@@ -47,232 +47,139 @@ 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);
loadTeam(settingsManager.getDataMode()); List<String> options = new ArrayList<>();
options.add("Individual");
options.add("Compiled");
options.add("History");
binding.dataTypeSpinner.setOptions(options, SettingsManager.getTeamsDataMode());
binding.dataTypeSpinner.setOnClickListener((item, index) -> {
SettingsManager.setTeamsDataMode(index);
loadTeam(index);
});
binding.tbaButton.setOnClickListener(v -> openWebPage(
"https://www.thebluealliance.com/team/"+team.teamNumber+"/"+SettingsManager.getYearNum()
));
binding.statboticsButton.setOnClickListener(v -> openWebPage(
"https://www.statbotics.io/team/"+team.teamNumber+"/"+SettingsManager.getYearNum()
));
loadTeam(SettingsManager.getTeamsDataMode());
return binding.getRoot(); return binding.getRoot();
} }
public void openWebPage(String url) {
Uri webpage = Uri.parse(url);
Intent intent = new Intent(Intent.ACTION_VIEW, webpage);
// if (intent.resolveActivity(getActivity().getPackageManager()) != null) {
startActivity(intent);
// }
}
public void loadTeam(int mode) { public void loadTeam(int mode) {
binding.teamsArea.removeAllViews(); binding.dataTeamCard.fromTeam(team);
LinearLayout ll = new LinearLayout(getContext());
ll.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
ll.setOrientation(LinearLayout.VERTICAL);
binding.teamsArea.addView(ll);
PowerSpinnerView dropdown = new PowerSpinnerView(getContext()); try {add_pit_data(team);}catch(Exception e){AlertManager.error(e);}
try {add_match_data(team, mode);}catch(Exception e){AlertManager.error(e);}
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);
TextView tv = new TextView(getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText(String.valueOf(team.teamNumber));
tv.setTextSize(28);
ll.addView(tv);
tv = new TextView(getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText(team.teamName);
tv.setTextSize(28);
ll.addView(tv);
tv = new TextView(getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText(team.getDescription());
tv.setTextSize(16);
ll.addView(tv);
add_pit_data(ll, team);
add_match_data(ll, team, mode);
} }
public void add_pit_data(LinearLayout ll, frcTeam team){ public void add_pit_data(frcTeam team) throws BuiltByteParser.byteParsingExeption {
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()); binding.pitArea.addView(new TextViewBuilder(getContext(), "No pit data has been collected!")
tv.setLayoutParams(new FrameLayout.LayoutParams( .layout_match_wrap().align_center().size(23).build());
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText("No pit data has been collected!");
tv.setTextSize(23);
ll.addView(tv);
return; 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());
tv.setLayoutParams(new FrameLayout.LayoutParams( binding.pitArea.addView(new TextViewBuilder(getContext(), "Pit scouting by " + psda.username)
ViewGroup.LayoutParams.MATCH_PARENT, .layout_match_wrap()
ViewGroup.LayoutParams.WRAP_CONTENT .padding(0, 20, 0, 5)
)); .align_center()
tv.setPadding(0, 20, 0, 5); .size(30)
tv.setGravity(Gravity.CENTER_HORIZONTAL); .build()
tv.setText("Pit scouting by " + psda.username); );
tv.setTextSize(30);
ll.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()); TextViewBuilder tvb = new TextViewBuilder(getContext(), pit_latest_values[a].name)
tv.setLayoutParams(new FrameLayout.LayoutParams( .align_center()
ViewGroup.LayoutParams.MATCH_PARENT, .layout_match_wrap()
ViewGroup.LayoutParams.WRAP_CONTENT .size(25);
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText(psda.data.array[a].getName());
tv.setTextSize(25);
if(psda.data.array[a].isNull()){ if(psda.data.array[a].isNull()){
tv.setBackgroundColor(0xffff0000); tvb.tv.setBackgroundColor(toggletitle_unselected);
tv.setTextColor(0xff000000); tvb.tv.setTextColor(toggletitle_black_background);
} }
binding.pitArea.addView(tvb.build());
ll.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();
String[] files = fileEditor.getMatchesByTeamNum(evcode, team.teamNumber); binding.individualViewSelector.setVisibility(View.GONE);
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()); binding.matchArea.addView(new TextViewBuilder(getContext(), "No match data has been collected!")
tv.setLayoutParams(new FrameLayout.LayoutParams( .layout_match_wrap()
ViewGroup.LayoutParams.MATCH_PARENT, .align_center()
ViewGroup.LayoutParams.WRAP_CONTENT .size(23)
)); .build());
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText("No match data has been collected!");
tv.setTextSize(23);
ll.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,45 +187,70 @@ 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("-");
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]); int match_num = Integer.parseInt(split[1]);
binding.matchNum.setText(split[1]);
ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(files[i], match_values, match_transferValues); ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(files[matchIndex], match_values, match_transferValues);
TextView tv = new TextView(getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setPadding(0, 40, 0, 5);
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText("M" + (match_num) + " " + split[2] + "-" + split[3] + " by " + psda.username);
tv.setTextSize(30);
ll.addView(tv);
for (int a = 0; a < psda.data.array.length; a++) { binding.matchArea.addView(
tv = new TextView(getContext()); new TextViewBuilder(getContext(), "M" + (match_num) + " " + split[2] + "-" + split[3] + " by " + psda.username)
tv.setLayoutParams(new FrameLayout.LayoutParams( .align_center()
ViewGroup.LayoutParams.MATCH_PARENT, .size(30)
ViewGroup.LayoutParams.WRAP_CONTENT .padding(0,0,40,5)
)); .build()
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText(psda.data.array[a].getName());
tv.setTextSize(25);
if (psda.data.array[a].isNull()) { );
tv.setBackgroundColor(0xffff0000);
tv.setTextColor(0xff000000);
for (int i = 0; i < psda.data.array.length; i++) {
TextViewBuilder tv = new TextViewBuilder(getContext(), match_latest_values[i].name)
.align_center()
.size(25);
if (psda.data.array[i].isNull()) {
tv.tv.setBackgroundColor(toggletitle_unselected);
tv.tv.setTextColor(toggletitle_black_background);
} }
ll.addView(tv); binding.matchArea.addView(tv.build());
match_latest_values[a].add_individual_view(ll, psda.data.array[a]); if(psda.data.array[i] != null)
match_latest_values[i].add_individual_view(binding.matchArea, psda.data.array[i]);
} }
}catch (Exception e){
AlertManager.error("Failure to load file " + files[matchIndex], e);
} }
} }
@@ -326,29 +258,32 @@ 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]; RawDataType[][] data = new RawDataType[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++) {
if(psda.data.array[a] != null && psda.data.array[a].get() != null)
data[a][i] = psda.data.array[a]; data[a][i] = psda.data.array[a];
} }
} catch (Exception e){
AlertManager.error("Failure to load file " + files[i], e);
}
} }
for(int i = 0; i < match_latest_values.length; i++){ for(int i = 0; i < match_latest_values.length; i++){
TextView tv = new TextView(getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setPadding(0, 20, 0, 5);
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText(match_latest_values[i].name);
tv.setTextSize(30);
ll.addView(tv);
match_latest_values[i].add_compiled_view(ll, data[i]); binding.matchArea.addView(
new TextViewBuilder(getContext(), match_latest_values[i].name)
.align_center()
.padding(0, 0, 20, 5)
.size(30)
.build()
);
if(data[i] != null)
match_latest_values[i].add_compiled_view(binding.matchArea, data[i]);
} }
} }
@@ -356,29 +291,33 @@ 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]; RawDataType[][] data = new RawDataType[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++) {
if(psda.data.array[a] != null && psda.data.array[a].get() != null)
data[a][i] = psda.data.array[a]; data[a][i] = psda.data.array[a];
} }
}catch (Exception e){
AlertManager.error("Failure to load file " + files[i], e);
}
} }
for(int i = 0; i < match_latest_values.length; i++){ for(int i = 0; i < match_latest_values.length; i++){
TextView tv = new TextView(getContext());
tv.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setPadding(0, 20, 0, 5);
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText(match_latest_values[i].name);
tv.setTextSize(30);
ll.addView(tv);
match_latest_values[i].add_history_view(ll, data[i]); binding.matchArea.addView(
new TextViewBuilder(getContext(), match_latest_values[i].name)
.align_center()
.size(30)
.padding(0,0,20,5)
.build()
);
if(data[i] != null)
match_latest_values[i].add_history_view(binding.matchArea, data[i]);
} }
} }
} }
@@ -0,0 +1,409 @@
package com.ridgebotics.ridgescout.ui.scouting;
import static com.ridgebotics.ridgescout.utility.Colors.color_found;
import static com.ridgebotics.ridgescout.utility.Colors.color_not_found;
import static com.ridgebotics.ridgescout.utility.Colors.color_rescout;
import static com.ridgebotics.ridgescout.utility.DataManager.event;
import android.app.AlertDialog;
import android.os.Bundle;
import android.text.InputType;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
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.fragment.app.Fragment;
import com.ridgebotics.ridgescout.databinding.FragmentScoutingEventBinding;
import com.ridgebotics.ridgescout.types.frcTeam;
import com.ridgebotics.ridgescout.ui.views.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 com.ridgebotics.ridgescout.utility.builders.TextViewBuilder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
// Fragment to show the status of scouting, aswell as adding or removing matches and teams manually.
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);
}
private void addTableText(TableRow tr, String textStr){
tr.addView(new TextViewBuilder(getContext(), textStr)
.align_center()
.size(18)
.build());
}
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());
}
TextViewBuilder text = new TextViewBuilder(getContext(), String.valueOf(num))
.size(18)
.align_center();
final String filename = event.eventCode + "-" + num + ".pitscoutdata";
if(FileEditor.fileExist(filename)){
final boolean[] rescout = {DataManager.rescout_list.contains(filename)};
text.tv.setBackgroundColor(rescout[0] ? color_rescout : color_found);
text.tv.setOnLongClickListener(view -> {
rescout[0] = !rescout[0];
if(rescout[0]) {
text.tv.setBackgroundColor(color_rescout);
DataManager.rescout_list.add(filename);
}else{
text.tv.setBackgroundColor(color_found);
DataManager.rescout_list.remove(filename);
}
DataManager.save_rescout_list();
return true;
});
}else{
text.tv.setBackgroundColor(color_not_found);
}
tr.addView(text.build());
}
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++){
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);
}
TextViewBuilder text = new TextViewBuilder(getContext(), String.valueOf(team_num))
.size(18)
.align_center();
final String filename = event.eventCode + "-" + match.matchIndex + "-" + alliance_position + "-" + team_num + ".matchscoutdata";
if(FileEditor.fileExist(filename)){
final boolean[] rescout = {DataManager.rescout_list.contains(filename)};
text.tv.setBackgroundColor(rescout[0] ? color_rescout : color_found);
text.tv.setOnLongClickListener(view -> {
rescout[0] = !rescout[0];
if(rescout[0]) {
text.tv.setBackgroundColor(color_rescout);
DataManager.rescout_list.add(filename);
}else{
text.tv.setBackgroundColor(color_found);
DataManager.rescout_list.remove(filename);
}
DataManager.save_rescout_list();
return true;
});
}else{
text.tv.setBackgroundColor(color_not_found);
}
tr.addView(text.build());
}
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();
Log.i(getClass().toString(), String.valueOf(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;
}
}
}
@@ -1,10 +1,15 @@
package com.ridgebotics.ridgescout.ui.scouting; package com.ridgebotics.ridgescout.ui.scouting;
import static com.ridgebotics.ridgescout.utility.AutoSaveManager.AUTO_SAVE_DELAY;
import static com.ridgebotics.ridgescout.utility.Colors.rescout_color;
import static com.ridgebotics.ridgescout.utility.Colors.saved_color;
import static com.ridgebotics.ridgescout.utility.Colors.unsaved_color;
import static com.ridgebotics.ridgescout.utility.DataManager.evcode; import static com.ridgebotics.ridgescout.utility.DataManager.evcode;
import static com.ridgebotics.ridgescout.utility.DataManager.event; import static com.ridgebotics.ridgescout.utility.DataManager.event;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@@ -14,21 +19,23 @@ 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.utility.settingsManager; import com.google.android.material.divider.MaterialDivider;
import com.ridgebotics.ridgescout.ui.views.ToggleTitleView;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.SettingsManager;
import com.ridgebotics.ridgescout.databinding.FragmentScoutingMatchBinding; import com.ridgebotics.ridgescout.databinding.FragmentScoutingMatchBinding;
import com.ridgebotics.ridgescout.scoutingData.ScoutingDataWriter; import com.ridgebotics.ridgescout.scoutingData.ScoutingDataWriter;
import com.ridgebotics.ridgescout.types.data.dataType; import com.ridgebotics.ridgescout.types.data.RawDataType;
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.types.input.FieldType;
import com.ridgebotics.ridgescout.utility.AlertManager; import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.AutoSaveManager; import com.ridgebotics.ridgescout.utility.AutoSaveManager;
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.ridgebotics.ridgescout.utility.builders.TextViewBuilder;
import java.util.ArrayList;
import java.util.function.Function;
// Fragment for match scouting data editing.
public class MatchScoutingFragment extends Fragment { public class MatchScoutingFragment extends Fragment {
private FragmentScoutingMatchBinding binding; private FragmentScoutingMatchBinding binding;
@@ -41,59 +48,55 @@ public class MatchScoutingFragment extends Fragment {
DataManager.reload_match_fields(); DataManager.reload_match_fields();
alliance_position = settingsManager.getAllyPos(); alliance_position = SettingsManager.getAllyPos();
username = settingsManager.getUsername(); username = SettingsManager.getUsername();
binding.username.setText(username); binding.bindicator.setUsername(username);
binding.alliancePosText.setText(alliance_position); binding.bindicator.setAlliancePos(alliance_position);
binding.bindicator.bringToFront();
binding.teamDescription.setVisibility(View.GONE); binding.matchTeamCard.setVisibility(View.GONE);
binding.teamName.setVisibility(View.GONE);
clear_fields(); clear_fields();
binding.teamDescription.setVisibility(View.VISIBLE); binding.matchTeamCard.setVisibility(View.VISIBLE);
binding.teamName.setVisibility(View.VISIBLE);
if(DataManager.match_values == null || DataManager.match_values.length == 0){ if(DataManager.match_values == null || DataManager.match_values.length == 0){
TextView tv = new TextView(getContext());
tv.setText("Failed to load fields.\nTry to either download or create match scouting fields."); binding.MatchScoutArea.addView(
tv.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); new TextViewBuilder(getContext(), "Failed to load fields.\nTry to either download or create match scouting fields.")
binding.MatchScoutArea.addView(tv); .align_center()
.build());
return binding.getRoot(); return binding.getRoot();
} }
binding.bindicator.match_indicator_next_button.setOnClickListener(v -> {
cur_match_num = settingsManager.getMatchNum();
update_match_num();
binding.nextButton.setOnClickListener(v -> {
if(edited) save(); if(edited) save();
settingsManager.setMatchNum(cur_match_num+1); SettingsManager.setMatchNum(cur_match_num+1);
cur_match_num += 1; cur_match_num += 1;
update_match_num(); update_match_num();
update_scouting_data(); update_scouting_data();
}); });
if(SettingsManager.getEnableQuickAlliancePosChange())
boolean fileIndicatorTapped = false; binding.bindicator.setOnClickListener(v -> {
binding.fileIndicator.setOnClickListener(v -> {
// if(e.getAction() != MotionEvent.ACTION_MOVE) return true; // if(e.getAction() != MotionEvent.ACTION_MOVE) return true;
// System.out.println(e.getAxisValue(0)); // System.out.println(e.getAxisValue(0));
if(edited) save(); if(edited) save();
alliance_position = incrementMatchPos(alliance_position); alliance_position = incrementMatchPos(alliance_position);
settingsManager.setAllyPos(alliance_position); SettingsManager.setAllyPos(alliance_position);
binding.alliancePosText.setText(alliance_position); binding.bindicator.setAlliancePos(alliance_position);
update_match_num(); update_match_num();
update_scouting_data(); update_scouting_data();
// return true; // return true;
}); });
binding.backButton.setOnClickListener(v -> { binding.bindicator.match_indicator_back_button.setOnClickListener(v -> {
if(edited) save(); if(edited) save();
settingsManager.setMatchNum(cur_match_num-1); SettingsManager.setMatchNum(cur_match_num-1);
cur_match_num -= 1; cur_match_num -= 1;
update_match_num(); update_match_num();
update_scouting_data(); update_scouting_data();
@@ -103,9 +106,22 @@ 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();
if(DataManager.scoutNotice.isEmpty())
binding.scoutingNoticeBox.setVisibility(View.GONE);
else
binding.scoutingNoticeText.setText(DataManager.scoutNotice);
return binding.getRoot(); return binding.getRoot();
} }
@@ -127,40 +143,35 @@ public class MatchScoutingFragment extends Fragment {
return "red-1"; return "red-1";
} }
private static final int unsaved_color = 0x60ff0000;
private static final int saved_color = 0x6000ff00;
String alliance_position; String alliance_position;
int cur_match_num; int cur_match_num;
String username; String username;
String fileUsernames = "";
String filename; String filename;
boolean edited = false; boolean edited = false;
boolean rescout = false;
TextView[] titles; ToggleTitleView[] titles;
AutoSaveManager asm = new AutoSaveManager(this::save, AUTO_SAVE_DELAY);
AutoSaveManager asm = new AutoSaveManager(this::save);
ArrayList<dataType> dataTypes;
public void save(){ public void save(){
System.out.println("Saved!"); Log.i(this.getClass().toString(), "Saved!");
edited = false; edited = false;
set_indicator_color(saved_color); enableRescoutButton();
AlertManager.toast("Saved " + filename); AlertManager.toast("Saved " + filename);
save_fields(); save_fields();
} }
public void set_indicator_color(int color){ public void set_indicator_color(int color){
binding.fileIndicator.setBackgroundColor(color); binding.bindicator.setColor(color);
} }
public void update_asm(){ public void update_asm(){
// v.getBackground().setColorFilter(Color.parseColor("#00ff00"), PorterDuff.Mode.DARKEN); // v.getBackground().setColorFilter(Color.parseColor("#00ff00"), PorterDuff.Mode.DARKEN);
edited = true; edited = true;
set_indicator_color(unsaved_color); set_indicator_color(unsaved_color);
disableRescoutButton();
asm.update(); asm.update();
} }
@@ -186,48 +197,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);
} }
} }
@@ -240,18 +244,18 @@ public class MatchScoutingFragment extends Fragment {
edited = false; edited = false;
binding.matchnum.setText(String.valueOf(cur_match_num+1)); binding.bindicator.setMatchNum(String.valueOf(cur_match_num+1));
if(cur_match_num <= 0){ if(cur_match_num <= 0){
binding.backButton.setVisibility(View.GONE); binding.bindicator.match_indicator_back_button.setVisibility(View.GONE);
}else{ }else{
binding.backButton.setVisibility(View.VISIBLE); binding.bindicator.match_indicator_back_button.setVisibility(View.VISIBLE);
} }
if(cur_match_num >= event.matches.size()-1){ if(cur_match_num >= event.matches.size()-1){
binding.nextButton.setVisibility(View.GONE); binding.bindicator.match_indicator_next_button.setVisibility(View.GONE);
}else{ }else{
binding.nextButton.setVisibility(View.VISIBLE); binding.bindicator.match_indicator_next_button.setVisibility(View.VISIBLE);
} }
} }
@@ -273,7 +277,7 @@ public class MatchScoutingFragment extends Fragment {
break; break;
} }
binding.barTeamNum.setText(String.valueOf(team_num)); binding.bindicator.setTeamNum(String.valueOf(team_num));
frcTeam team = null; frcTeam team = null;
for(int i=0; i < event.teams.size(); i++){ for(int i=0; i < event.teams.size(); i++){
@@ -286,6 +290,8 @@ public class MatchScoutingFragment extends Fragment {
filename = evcode + "-" + (cur_match_num+1) + "-" + alliance_position + "-" + team_num + ".matchscoutdata"; filename = evcode + "-" + (cur_match_num+1) + "-" + alliance_position + "-" + team_num + ".matchscoutdata";
rescout = DataManager.rescout_list.contains(filename);
return team; return team;
} }
@@ -297,10 +303,16 @@ 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);
binding.teamName.setText(team.teamName); if(team == null) {
binding.teamDescription.setText(team.getDescription()); AlertManager.addSimpleError("This team does not exist!");
binding.matchTeamCard.setTeamName("Error!");
binding.matchTeamCard.setTeamDescription("Error!");
return;
}
boolean new_file = !fileEditor.fileExist(filename); binding.matchTeamCard.fromTeam(team);
boolean new_file = !FileEditor.fileExist(filename);
if(asm.isRunning){ if(asm.isRunning){
asm.stop(); asm.stop();
@@ -309,9 +321,17 @@ public class MatchScoutingFragment extends Fragment {
if(new_file){ if(new_file){
default_fields(); default_fields();
set_indicator_color(unsaved_color); set_indicator_color(unsaved_color);
disableRescoutButton();
}else{ }else{
try {
get_fields(); get_fields();
set_indicator_color(saved_color); enableRescoutButton();
} catch (Exception e){
AlertManager.error(e);
default_fields();
set_indicator_color(unsaved_color);
disableRescoutButton();
}
} }
asm.start(); asm.start();
@@ -322,20 +342,21 @@ public class MatchScoutingFragment extends Fragment {
public void default_fields(){ public void default_fields(){
for(int i = 0; i < DataManager.match_latest_values.length; i++){ for(int i = 0; i < DataManager.match_latest_values.length; i++){
inputType input = DataManager.match_latest_values[i]; FieldType 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);
} }
} }
public void get_fields(){ public void get_fields() throws BuiltByteParser.byteParsingExeption{
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; RawDataType[] types = psdr.data.array;
fileUsernames = psdr.username;
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();
@@ -346,14 +367,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);
}
} }
} }
@@ -361,15 +376,37 @@ public class MatchScoutingFragment extends Fragment {
public void save_fields(){ public void save_fields(){
dataType[] types = new dataType[DataManager.match_latest_values.length]; RawDataType[] types = new RawDataType[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++){
types[i] = DataManager.match_latest_values[i].getViewValue(); types[i] = DataManager.match_latest_values[i].getViewValue();
} }
if(ScoutingDataWriter.save(DataManager.match_values.length-1, username, filename, types)) if(ScoutingDataWriter.save(DataManager.match_values.length-1, ScoutingDataWriter.checkAddName(fileUsernames, username), filename, types))
System.out.println("Saved!"); Log.i(getClass().toString(), "Saved!");
else else
System.out.println("Error saving"); Log.i(getClass().toString(), "Error saving");
}
private void enableRescoutButton(){
set_indicator_color(rescout ? rescout_color : saved_color);
binding.bindicator.setOnLongClickListener(v -> {
rescout = !rescout;
if(rescout){
set_indicator_color(rescout_color);
DataManager.rescout_list.add(filename);
DataManager.save_rescout_list();
}else{
set_indicator_color(saved_color);
DataManager.rescout_list.remove(filename);
DataManager.save_rescout_list();
}
return true;
});
}
private void disableRescoutButton(){
binding.bindicator.setOnLongClickListener(null);
} }
} }
@@ -1,33 +1,43 @@
package com.ridgebotics.ridgescout.ui.scouting; package com.ridgebotics.ridgescout.ui.scouting;
import static com.ridgebotics.ridgescout.utility.AutoSaveManager.AUTO_SAVE_DELAY;
import static com.ridgebotics.ridgescout.utility.Colors.rescout_color;
import static com.ridgebotics.ridgescout.utility.Colors.saved_color;
import static com.ridgebotics.ridgescout.utility.Colors.unsaved_color;
import static com.ridgebotics.ridgescout.utility.DataManager.evcode; import static com.ridgebotics.ridgescout.utility.DataManager.evcode;
import static com.ridgebotics.ridgescout.utility.DataManager.pit_latest_values; import static com.ridgebotics.ridgescout.utility.DataManager.pit_latest_values;
import static com.ridgebotics.ridgescout.utility.DataManager.pit_transferValues; import static com.ridgebotics.ridgescout.utility.DataManager.pit_transferValues;
import static com.ridgebotics.ridgescout.utility.DataManager.pit_values; import static com.ridgebotics.ridgescout.utility.DataManager.pit_values;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log;
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.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.utility.settingsManager; import com.google.android.material.divider.MaterialDivider;
import com.ridgebotics.ridgescout.ui.views.PitScoutingIndicator;
import com.ridgebotics.ridgescout.ui.views.ToggleTitleView;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.SettingsManager;
import com.ridgebotics.ridgescout.databinding.FragmentScoutingPitBinding; import com.ridgebotics.ridgescout.databinding.FragmentScoutingPitBinding;
import com.ridgebotics.ridgescout.scoutingData.ScoutingDataWriter; import com.ridgebotics.ridgescout.scoutingData.ScoutingDataWriter;
import com.ridgebotics.ridgescout.types.data.dataType; import com.ridgebotics.ridgescout.types.data.RawDataType;
import com.ridgebotics.ridgescout.types.frcTeam; import com.ridgebotics.ridgescout.types.frcTeam;
import com.ridgebotics.ridgescout.types.input.inputType; import com.ridgebotics.ridgescout.types.input.FieldType;
import com.ridgebotics.ridgescout.utility.AutoSaveManager; import com.ridgebotics.ridgescout.utility.AutoSaveManager;
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 java.util.ArrayList; import java.util.ArrayList;
import java.util.function.Function; import java.util.function.Function;
// Fragment for pit scouting data editing
public class PitScoutingFragment extends Fragment { public class PitScoutingFragment extends Fragment {
FragmentScoutingPitBinding binding; FragmentScoutingPitBinding binding;
@@ -43,51 +53,61 @@ public class PitScoutingFragment extends Fragment {
binding = FragmentScoutingPitBinding.inflate(inflater, container, false); binding = FragmentScoutingPitBinding.inflate(inflater, container, false);
username = settingsManager.getUsername(); username = SettingsManager.getUsername();
DataManager.reload_pit_fields(); DataManager.reload_pit_fields();
if(pit_latest_values == null) {
AlertManager.addSimpleError("Error loading pit fields!");
return binding.getRoot();
}
if(DataManager.scoutNotice.isEmpty())
binding.scoutingNoticeBox.setVisibility(View.GONE);
else
binding.scoutingNoticeText.setText(DataManager.scoutNotice);
loadTeam(); loadTeam();
return binding.getRoot(); return binding.getRoot();
} }
private static final int unsaved_color = 0x60ff0000;
private static final int saved_color = 0x6000ff00;
boolean edited = false; boolean edited = false;
boolean rescout = false;
String filename; String filename;
String username; String username;
TextView[] titles; String fileUsernames = "";
ToggleTitleView[] titles;
AutoSaveManager asm = new AutoSaveManager(this::save, AUTO_SAVE_DELAY);
AutoSaveManager asm = new AutoSaveManager(this::save); ArrayList<RawDataType> rawDataTypes;
ArrayList<dataType> dataTypes;
public void save(){ public void save(){
edited = false; edited = false;
set_indicator_color(saved_color); enableRescoutButton();
dataType[] types = new dataType[pit_latest_values.length]; RawDataType[] types = new RawDataType[pit_latest_values.length];
for(int i = 0; i < pit_latest_values.length; i++){ for(int i = 0; i < pit_latest_values.length; i++){
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, ScoutingDataWriter.checkAddName(fileUsernames, username), filename, types)) {
System.out.println("Saved!"); Log.i(getClass().toString(), "Saved!");
else Log.i(getClass().toString(), "Saved " + filename);
System.out.println("Error saving"); }else
Log.i(getClass().toString(), "Error saving");
} }
public void set_indicator_color(int color){ public void set_indicator_color(int color){
binding.pitFileIndicator.setBackgroundColor(color); binding.pitIndicator.setColor(color);
} }
public void update_asm(){ public void update_asm(){
// v.getBackground().setColorFilter(Color.parseColor("#00ff00"), PorterDuff.Mode.DARKEN); // v.getBackground().setColorFilter(Color.parseColor("#00ff00"), PorterDuff.Mode.DARKEN);
edited = true; edited = true;
set_indicator_color(unsaved_color); set_indicator_color(unsaved_color);
disableRescoutButton();
asm.update(); asm.update();
} }
@@ -95,17 +115,13 @@ public class PitScoutingFragment extends Fragment {
public void loadTeam(){ public void loadTeam(){
// clear_fields(); // clear_fields();
binding.pitFileIndicator.setVisibility(View.VISIBLE); binding.pitsTeamCard.setVisibility(View.VISIBLE);
binding.pitTeamName.setVisibility(View.VISIBLE); binding.pitIndicator.setTeamNum(team.teamNumber);
binding.pitTeamDescription.setVisibility(View.VISIBLE); binding.pitIndicator.setUsername(SettingsManager.getUsername());
binding.pitsTeamCard.fromTeam(team);
binding.pitTeamName.setText(team.teamName);
binding.pitTeamDescription.setText(team.getDescription());
binding.pitBarTeamNum.setText(String.valueOf(team.teamNumber));
filename = evcode + "-" + team.teamNumber + ".pitscoutdata"; filename = evcode + "-" + team.teamNumber + ".pitscoutdata";
rescout = DataManager.rescout_list.contains(filename);
boolean new_file = !fileEditor.fileExist(filename);
if(asm.isRunning){ if(asm.isRunning){
asm.stop(); asm.stop();
@@ -113,19 +129,50 @@ public class PitScoutingFragment extends Fragment {
create_fields(); create_fields();
if(new_file){ if(!FileEditor.fileExist(filename)){
default_fields(); default_fields();
set_indicator_color(unsaved_color); set_indicator_color(unsaved_color);
disableRescoutButton();
}else{ }else{
try {
get_fields(); get_fields();
set_indicator_color(saved_color);
enableRescoutButton();
} catch (Exception e){
AlertManager.error(e);
default_fields();
set_indicator_color(unsaved_color);
} }
}
binding.pitIndicator.bringToFront();
asm.start(); asm.start();
} }
private int default_text_color = 0; private void enableRescoutButton(){
set_indicator_color(rescout ? rescout_color : saved_color);
binding.pitIndicator.setOnLongClickListener(v -> {
rescout = !rescout;
if(rescout){
set_indicator_color(rescout_color);
DataManager.rescout_list.add(filename);
DataManager.save_rescout_list();
}else{
set_indicator_color(saved_color);
DataManager.rescout_list.remove(filename);
DataManager.save_rescout_list();
}
return true;
});
}
private void disableRescoutButton(){
binding.pitIndicator.setOnLongClickListener(null);
}
private void create_fields() { private void create_fields() {
@@ -133,37 +180,32 @@ public class PitScoutingFragment extends Fragment {
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);
} }
}); });
View v = pit_latest_values[i].createView(getContext(), new Function<dataType, Integer>() { View v = pit_latest_values[i].createView(getContext(), new Function<RawDataType, Integer>() {
@Override @Override
public Integer apply(dataType dataType) { public Integer apply(RawDataType dataType) {
// edited = true; // edited = true;
if(asm.isRunning) if(asm.isRunning)
update_asm(); update_asm();
@@ -177,29 +219,26 @@ public class PitScoutingFragment extends Fragment {
public void default_fields(){ public void default_fields(){
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]; FieldType 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);
} }
} }
public void get_fields(){ public void get_fields() throws BuiltByteParser.byteParsingExeption{
ScoutingDataWriter.ParsedScoutingDataResult psdr = ScoutingDataWriter.load(filename, pit_values, pit_transferValues); ScoutingDataWriter.ParsedScoutingDataResult psdr = ScoutingDataWriter.load(filename, pit_values, pit_transferValues);
dataType[] types = psdr.data.array; RawDataType[] types = psdr.data.array;
fileUsernames = psdr.username;
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);
} }
} }
} }
@@ -0,0 +1,139 @@
package com.ridgebotics.ridgescout.ui.scouting;
import static com.ridgebotics.ridgescout.utility.Colors.color_found;
import static com.ridgebotics.ridgescout.utility.Colors.color_not_found;
import static com.ridgebotics.ridgescout.utility.Colors.color_rescout;
import static com.ridgebotics.ridgescout.utility.DataManager.evcode;
import static com.ridgebotics.ridgescout.utility.DataManager.event;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TableLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.databinding.FragmentTeamSelectorBinding;
import com.ridgebotics.ridgescout.types.frcTeam;
import com.ridgebotics.ridgescout.ui.views.TeamListOption;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.DataManager;
import com.ridgebotics.ridgescout.utility.FileEditor;
import java.util.Arrays;
// Fragment for choosing which team to pit scout
public class PitSelectorFragment extends Fragment {
private FragmentTeamSelectorBinding binding;
private static onTeamSelected onSelect = new onTeamSelected() {@Override public void onSelect(PitSelectorFragment self, frcTeam team) {}};
public interface onTeamSelected {
void onSelect(PitSelectorFragment self, frcTeam team);
}
public static void setOnSelect(onTeamSelected tmponSelect){
onSelect = tmponSelect;
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentTeamSelectorBinding.inflate(inflater, container, false);
// event = fileEditor.g
DataManager.reload_event();
if(evcode == null || evcode.equals("unset")){
AlertManager.addSimpleError("You somehow have not loaded an event!");
return binding.getRoot();
}
load_teams();
return binding.getRoot();
}
public void load_teams(){
// binding.pitFileIndicator.setVisibility(View.GONE);
// binding.pitTeamName.setVisibility(View.GONE);
// binding.pitTeamDescription.setVisibility(View.GONE);
//
// clear_fields();
int[] teamNums = new int[event.teams.size()];
for(int i = 0 ; i < event.teams.size(); i++){
teamNums[i] = event.teams.get(i).teamNumber;
}
Arrays.sort(teamNums);
TableLayout table = new TableLayout(getContext());
table.setStretchAllColumns(true);
binding.teams.addView(table);
for(int i = 0; i < event.teams.size(); i++){
frcTeam team = null;
for(int a = 0 ; a < event.teams.size(); a++){
if(event.teams.get(a).teamNumber == teamNums[i]){
team = event.teams.get(a);
break;
}
}
assert team != null;
// TableRow tr = new TableRow(getContext());
// TableLayout.LayoutParams rowParams = new TableLayout.LayoutParams(
// FrameLayout.LayoutParams.WRAP_CONTENT,
// FrameLayout.LayoutParams.WRAP_CONTENT
// );
// rowParams.setMargins(20,20,20,20);
// tr.setLayoutParams(rowParams);
// tr.setPadding(20,20,20,20);
// table.addView(tr);
TeamListOption teamRow = new TeamListOption(getContext());
table.addView(teamRow);
teamRow.fromTeam(team);
String filename = evcode + "-" + team.teamNumber + ".pitscoutdata";
if (FileEditor.fileExist(filename)) {
final boolean[] rescout = {DataManager.rescout_list.contains(filename)};
teamRow.setColor(DataManager.rescout_list.contains(filename) ? color_rescout : color_found);
teamRow.setOnLongClickListener(v -> {
rescout[0] = !rescout[0];
if(rescout[0]){
DataManager.rescout_list.add(filename);
teamRow.setColor(color_rescout);
DataManager.save_rescout_list();
}else{
DataManager.rescout_list.remove(filename);
teamRow.setColor(color_found);
DataManager.save_rescout_list();
}
return true;
});
} else {
teamRow.setColor(color_not_found);
teamRow.setOnLongClickListener(v -> true);
}
frcTeam finalTeam = team;
teamRow.setOnClickListener(v -> onSelect.onSelect(this, finalTeam));
}
}
}
@@ -1,27 +1,39 @@
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.evcode;
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 android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.R; import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.utility.settingsManager; import com.ridgebotics.ridgescout.types.frcEvent;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.FileEditor;
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.ui.TeamSelectorFragment;
import com.ridgebotics.ridgescout.utility.DataManager; import com.ridgebotics.ridgescout.utility.DataManager;
import com.ridgebotics.ridgescout.utility.builders.TextViewBuilder;
import java.util.ArrayList;
import java.util.Set;
// Main dashbord page
public class ScoutingFragment extends Fragment { public class ScoutingFragment extends Fragment {
private FragmentScoutingBinding binding; private FragmentScoutingBinding binding;
@@ -34,42 +46,87 @@ 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.textMatchAlliance.setVisibility(View.GONE);
binding.textName.setVisibility(View.GONE);
binding.infoBox.setVisibility(View.GONE);
binding.textRescoutIndicator.setVisibility(View.GONE);
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);
}); });
binding.pitScoutingButton.setOnClickListener(v -> { binding.pitScoutingButton.setOnClickListener(v -> {
TeamSelectorFragment.setPits_mode(true); PitSelectorFragment.setOnSelect((self, team) -> {
TeamSelectorFragment.setOnSelect(new TeamSelectorFragment.onTeamSelected() {
@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_scouting_pit_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_scouting_pit_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);
}); });
updateDashboard();
return binding.getRoot(); return binding.getRoot();
} }
@@ -83,24 +140,45 @@ 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;
}
}); });
} }
private void updateDashboard() {
binding.textName.setText("Welcome, " + SettingsManager.getUsername() + "!");
int curMatchNum = SettingsManager.getMatchNum();
int nextMatch;
int teamNum = SettingsManager.getTeamNum();
try {
nextMatch = event.getNextTeamMatch(teamNum, curMatchNum).matchIndex;
} catch (Exception e){
AlertManager.error("Sorry, in event ("+evcode+"), your team number ("+teamNum+") wasn't found!", e);
return;
}
binding.textMatchAlliance.setText("Match: " + (curMatchNum+1) + ", " + SettingsManager.getAllyPos());
binding.textRescoutIndicator.setText("Things to rescout: " + DataManager.rescout_list.size());
binding.infoBox.addView(new TextViewBuilder(getContext(), "Our next match: Match " + nextMatch)
.body1()
.build());
int informedBy = event.getMostInformedBy(teamNum, curMatchNum);
binding.infoBox.addView(new TextViewBuilder(getContext(), "Most informed by: Match " + informedBy)
.body1()
.build());
}
} }
@@ -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);
}
}
}
@@ -0,0 +1,473 @@
package com.ridgebotics.ridgescout.ui.settings;
import static android.text.InputType.TYPE_CLASS_NUMBER;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TableLayout;
import android.widget.TextView;
import com.ridgebotics.ridgescout.types.input.CheckboxType;
import com.ridgebotics.ridgescout.types.input.DropdownType;
import com.ridgebotics.ridgescout.types.input.FieldposType;
import com.ridgebotics.ridgescout.types.input.FieldType;
import com.ridgebotics.ridgescout.types.input.NumberType;
import com.ridgebotics.ridgescout.types.input.SliderType;
import com.ridgebotics.ridgescout.types.input.TallyType;
import com.ridgebotics.ridgescout.types.input.TextType;
import com.ridgebotics.ridgescout.ui.views.CustomSpinnerView;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.builders.TextViewBuilder;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
// Class to help with fields editor fragment, containing the defaults for each field.
public class FieldEditorHelper {
private enum parameterTypeEnum {
paramNumber,
paramString,
paramStringArray,
paramNumberArray,
paramDropdown
}
public static class parameterType {
public String name;
public parameterTypeEnum id;
}
public static class paramNumber extends parameterType{
public int val;
public paramNumber(String name, int val){
this.name = name + " (Number)";
this.val = val;
this.id = parameterTypeEnum.paramNumber;
}
}
public static class paramString extends parameterType {
public String val;
public paramString(String name, String val){
this.name = name + " (String)";
this.val = val;
this.id = parameterTypeEnum.paramString;
}
}
public static class paramStringArray extends parameterType{
public String[] val;
public paramStringArray(String name, String[] val){
this.name = name + " (String array)";
this.val = val;
this.id = parameterTypeEnum.paramStringArray;
}
}
public static class paramDropdown extends parameterType{
public List<String> options;
public int val;
public paramDropdown(String name, List<String> options, int val){
this.name = name + " (Dropdown)";
this.options = options;
this.val = val;
this.id = parameterTypeEnum.paramDropdown;
}
}
// public static class paramNumberArray extends parameterType{
// public int[] val;
// public paramNumberArray(String name, int[] val){
// this.name = name + " (Number array)";
// this.val = val;
// this.id = parameterTypeEnum.paramNumberArray;
// }
// }
public static final parameterType[] defaultSliderParams = new parameterType[]{
new paramString("Name", "New Slider"),
new paramString("Description", ""),
new paramNumber("Min", 0),
new paramNumber("Max", 10),
new paramNumber("Default Value", 5)
};
public static final parameterType[] defaultDropdownParams = new parameterType[]{
new paramString("Name", "New Dropdown"),
new paramString("Description", ""),
new paramStringArray("Default Value", new String[]{"Zero","One","Two","Three"}),
new paramNumber("Default Option", 0),
};
public static final parameterType[] defaultTextParams = new parameterType[]{
new paramString("Name", "New Text"),
new paramString("Description", ""),
new paramString("Default Value", "")
};
public static final parameterType[] defaultTallyParams = new parameterType[]{
new paramString("Name", "New Tally"),
new paramString("Description", ""),
new paramNumber("Default Value", 0)
};
public static final parameterType[] defaultNumberParams = new parameterType[]{
new paramString("Name", "New Number"),
new paramString("Description", ""),
new paramNumber("Default Value", 0)
};
public static final parameterType[] defaultCheckboxParam = new parameterType[]{
new paramString("Name", "New Checkbox"),
new paramString("Description", ""),
new paramNumber("Default Value ( 1 or 0 )", 0)
};
public static final parameterType[] defaultFieldPosParam = new parameterType[]{
new paramString("Name", "New Field Position"),
new paramString("Description", ""),
new paramNumber("Default X", 0),
new paramNumber("Default Y", 0)
};
private static parameterType[] getSliderParams(SliderType s){
return new parameterType[]{
new paramString("Name", s.name),
new paramString("Description", s.description),
new paramNumber("Min", s.min),
new paramNumber("Max", s.max),
new paramNumber("Default Value", (int) s.default_value)
};
}
private static parameterType[] getDropdownParams(DropdownType s){
return new parameterType[]{
new paramString("Name", s.name),
new paramString("Description", s.description),
new paramStringArray("Default Value",s.text_options),
new paramNumber("Default Option", (int) s.default_value),
};
}
private static parameterType[] getTextParams(TextType s){
return new parameterType[]{
new paramString("Name", s.name),
new paramString("Description", s.description),
new paramString("Default Value", (String) s.default_value)
};
}
private static parameterType[] getTallyParams(TallyType s){
return new parameterType[]{
new paramString("Name", s.name),
new paramString("Description", s.description),
new paramNumber("Default Value", (int) s.default_value)
};
}
private static parameterType[] getNumberParams(NumberType s){
return new parameterType[]{
new paramString("Name", s.name),
new paramString("Description", s.description),
new paramNumber("Default Value", (int) s.default_value)
};
}
private static parameterType[] getCheckboxParam(CheckboxType s){
return new parameterType[]{
new paramString("Name", s.name),
new paramString("Description", s.description),
new paramNumber("Default Value ( 1 or 0 )", (int) s.default_value)
};
}
private static parameterType[] getFieldPosParam(FieldposType s){
FieldposType.FieldImage[] f_images = FieldposType.FieldImage.values();
List<String> images = new ArrayList<>();
for (FieldposType.FieldImage fimage: f_images) {
images.add(fimage.toString());
}
return new parameterType[]{
new paramString("Name", s.name),
new paramString("Description", s.description),
new paramDropdown("Field Image", images, 0),
new paramNumber("Default X", ((int[]) s.default_value)[0]),
new paramNumber("Default Y", ((int[]) s.default_value)[1])
};
}
public static void setSliderParams(SliderType s, parameterType[] types){
s.name = ((paramString) types[0]).val;
s.description = ((paramString) types[1]).val;
s.min = ((paramNumber) types[2]).val;
s.max = ((paramNumber) types[3]).val;
s.default_value = ((paramNumber) types[4]).val;
}
public static void setDropdownParams(DropdownType s, parameterType[] types){
s.name = ((paramString) types[0]).val;
s.description = ((paramString) types[1]).val;
s.text_options = ((paramStringArray) types[2]).val;
s.default_value = ((paramNumber) types[3]).val;
}
public static void setTextParams(TextType s, parameterType[] types){
s.name = ((paramString) types[0]).val;
s.description = ((paramString) types[1]).val;
s.default_value = ((paramString) types[2]).val;
}
public static void setTallyParams(TallyType s, parameterType[] types){
s.name = ((paramString) types[0]).val;
s.description = ((paramString) types[1]).val;
s.default_value = ((paramNumber) types[2]).val;
}
public static void setNumberParams(NumberType s, parameterType[] types){
s.name = ((paramString) types[0]).val;
s.description = ((paramString) types[1]).val;
s.default_value = ((paramNumber) types[2]).val;
}
public static void setCheckboxParam(CheckboxType s, parameterType[] types){
s.name = ((paramString) types[0]).val;
s.description = ((paramString) types[1]).val;
s.default_value = ((paramNumber) types[2]).val;
}
public static void setFieldPosParam(FieldposType s, parameterType[] types){
s.name = ((paramString) types[0]).val;
s.description = ((paramString) types[1]).val;
s.fieldImage = FieldposType.FieldImage.from_index(((paramDropdown) types[2]).val);
s.default_value = new int[]{
((paramNumber) types[3]).val,
((paramNumber) types[4]).val
};
}
private static void setInputParameter(FieldType t, parameterType[] types){
switch (t.getInputType()){
case TALLY:
setTallyParams((TallyType) t, types);
break;
case SLIDER:
setSliderParams((SliderType) t, types);
break;
case DROPDOWN:
setDropdownParams((DropdownType) t, types);
break;
case NOTES_INPUT:
setTextParams((TextType) t, types);
break;
case NUMBER:
setNumberParams((NumberType) t, types);
break;
case CHECKBOX:
setCheckboxParam((CheckboxType) t, types);
break;
case FIELDPOS:
setFieldPosParam((FieldposType) t, types);
break;
}
}
private static parameterType[] getParamsFromInputType(FieldType t){
switch (t.getInputType()){
case TALLY:
return getTallyParams((TallyType) t);
case SLIDER:
return getSliderParams((SliderType) t);
case DROPDOWN:
return getDropdownParams((DropdownType) t);
case NOTES_INPUT:
return getTextParams((TextType) t);
case NUMBER:
return getNumberParams((NumberType) t);
case CHECKBOX:
return getCheckboxParam((CheckboxType) t);
case FIELDPOS:
return getFieldPosParam((FieldposType) t);
}
return new parameterType[]{};
}
private static View createNumberEdit(Context c, int value){
EditText text = new EditText(c);
text.setInputType(TYPE_CLASS_NUMBER);
text.setText(String.valueOf(value));
text.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
return text;
}
private static View createStringEdit(Context c, String value){
EditText text = new EditText(c);
text.setText(value);
text.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
return text;
}
private static View createStringArrayEdit(Context c, String[] value){
EditText text = new EditText(c);
text.setText(String.join("\n", value));
text.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
return text;
}
private static View createDropdown(Context c, String name, List<String> options, int value){
CustomSpinnerView spinner = new CustomSpinnerView(c);
spinner.setTitle(name);
spinner.setOptions(options, value);
spinner.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
// EditText text = new EditText(c);
// text.setText(String.join("\n", value));
// text.setLayoutParams(new LinearLayout.LayoutParams(
// ViewGroup.LayoutParams.MATCH_PARENT,
// ViewGroup.LayoutParams.WRAP_CONTENT
// ));
return spinner;
}
private static View createEdit(Context c, parameterType t){
switch (t.id){
case paramNumber:
return createNumberEdit(c, ((paramNumber) t).val);
case paramString:
return createStringEdit(c, ((paramString) t).val);
case paramStringArray:
return createStringArrayEdit(c, ((paramStringArray) t).val);
case paramDropdown:
return createDropdown(c, t.name, ((paramDropdown) t).options, ((paramDropdown) t).val);
}
return null;
}
private static boolean readEdit(View v, parameterType t){
try{
// String val;
switch (t.id) {
case paramNumber:
String val1 = ((EditText) v).getText().toString();
if(val1.isEmpty() || val1.isBlank()) return false;
((paramNumber) t).val = Integer.parseInt(val1);
break;
case paramString:
String val2 = ((EditText) v).getText().toString();
//if(val.isEmpty() || val.isBlank()) return false;
((paramString) t).val = val2;
break;
case paramStringArray:
String val3 = ((EditText) v).getText().toString();
if(val3.isEmpty() || val3.isBlank()) return false;
((paramStringArray) t).val = val3.split("\n");
break;
case paramDropdown:
int val4 = ((CustomSpinnerView) v).getIndex();
// if(val.isEmpty() || val.isBlank()) return false;
((paramDropdown) t).val = val4;
break;
}
} catch (Exception e) {
AlertManager.error(e);
return false;
}
return true;
}
public static FieldType createNewFieldType(int n){
switch(n){
case 0:
SliderType slider = new SliderType();
slider.UUID = UUID.randomUUID().toString();
setSliderParams(slider, defaultSliderParams);
return slider;
case 1:
TextType textType = new TextType();
textType.UUID = UUID.randomUUID().toString();
setTextParams(textType, defaultTextParams);
return textType;
case 2:
DropdownType dropdownType = new DropdownType();
dropdownType.UUID = UUID.randomUUID().toString();
setDropdownParams(dropdownType, defaultDropdownParams);
return dropdownType;
case 3:
TallyType tallyType = new TallyType();
tallyType.UUID = UUID.randomUUID().toString();
setTallyParams(tallyType, defaultTallyParams);
return tallyType;
case 4:
NumberType numberType = new NumberType();
numberType.UUID = UUID.randomUUID().toString();
setNumberParams(numberType, defaultNumberParams);
return numberType;
case 5:
CheckboxType checkboxType = new CheckboxType();
checkboxType.UUID = UUID.randomUUID().toString();
setCheckboxParam(checkboxType, defaultCheckboxParam);
return checkboxType;
case 6:
FieldposType fieldposType = new FieldposType();
fieldposType.UUID = UUID.randomUUID().toString();
setFieldPosParam(fieldposType, defaultFieldPosParam);
return fieldposType;
}
return null;
}
private parameterType[] types;
private View[] views;
private FieldType t;
public FieldEditorHelper(Context c, FieldType t, TableLayout parentView, parameterType[] tmptypes){
this.types = tmptypes;
this.t = t;
views = new View[types.length];
for(int i = 0; i < types.length; i++){
parentView.addView(new TextViewBuilder(c, types[i].name)
.align_center()
.size(20)
.build());
views[i] = createEdit(c, types[i]);
parentView.addView(views[i]);
}
}
public FieldEditorHelper(Context c, FieldType t, TableLayout parentView){
this(c,t,parentView,getParamsFromInputType(t));
}
public boolean save(){
for(int i = 0; i < types.length; i++){
if(!readEdit(views[i], types[i]))
return false;
}
setInputParameter(t, types);
return true;
}
}
@@ -0,0 +1,313 @@
package com.ridgebotics.ridgescout.ui.settings;
import static com.ridgebotics.ridgescout.utility.Colors.background_color;
import static com.ridgebotics.ridgescout.utility.Colors.unfocused_background_color;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ScrollView;
import android.widget.TableLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation;
import com.google.android.material.button.MaterialButton;
import com.ridgebotics.ridgescout.MainActivity;
import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.databinding.FragmentSettingsFieldsBinding;
import com.ridgebotics.ridgescout.scoutingData.Fields;
import com.ridgebotics.ridgescout.types.input.FieldType;
import com.ridgebotics.ridgescout.ui.views.CustomSpinnerView;
import com.ridgebotics.ridgescout.ui.views.FieldDisplay;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.builders.TextViewBuilder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
// Fragment that shows the field editor.
public class FieldsFragment extends Fragment {
FragmentSettingsFieldsBinding binding;
private static String filename;
public static void set_filename(String tmpfilename){
filename = tmpfilename;
}
private int index = -1;
private boolean edited = false;
List<FieldType> values;
List<FieldDisplay> views;
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentSettingsFieldsBinding.inflate(inflater, container, false);
binding.upButton.setEnabled(false);
binding.downButton.setEnabled(false);
binding.saveButton.setEnabled(false);
FieldType[][] tmp_values = Fields.load(filename);
if(tmp_values == null || tmp_values.length == 0) return binding.getRoot();
values = new ArrayList(List.of(tmp_values[tmp_values.length-1]));
views = new ArrayList<>();
for(int i = 0; i < values.size(); i++){
createFieldDisplay(values.get(i));
}
// Up and down buttons
binding.upButton.setOnClickListener(v -> {
if(index <= 0) return;
Collections.swap(values, index, index-1);
Collections.swap(views, index, index-1);
index--;
updateRowOrder();
});
binding.downButton.setOnClickListener(v -> {
if(index >= values.size()-1) return;
Collections.swap(values, index, index+1);
Collections.swap(views, index, index+1);
index++;
updateRowOrder();
});
// Add Field button
binding.addButton.setOnClickListener(v -> {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle("Select Type");
final CustomSpinnerView dropdown = new CustomSpinnerView(getContext());
List<String> options = new ArrayList<>();
options.add("Slider");
options.add("Text");
options.add("Dropdown");
options.add("Tally");
options.add("Number");
options.add("Checkbox");
options.add("Field Position");
dropdown.setOptions(options, 0);
dropdown.setTitle("Type");
builder.setView(dropdown);
builder.setNegativeButton("Cancel", (dialog, which) -> dialog.cancel());
builder.setPositiveButton("OK", (dialog, which) -> addField(dropdown.getIndex()));
builder.show();
});
// Back button listener
((MainActivity) getActivity()).setOnBackPressed(() -> {
if(!edited) 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) -> {
edited = false;
if(getActivity() != null)
getActivity().onBackPressed();
});
alert.setCancelable(true);
alert.create().show();
return false;
});
binding.saveButton.setOnClickListener(l -> save());
if(tmp_values.length > 1)
binding.revertButton.setOnClickListener(v -> revertPopup());
else
binding.revertButton.setEnabled(false);
return binding.getRoot();
}
@SuppressLint("ClickableViewAccessibility")
private void createFieldDisplay(FieldType field){
final FieldDisplay fd = new FieldDisplay(getContext());
views.add(fd);
fd.setField(field);
fd.setColor(unfocused_background_color);
fd.coloredBackground.setOnTouchListener((view, motionEvent) -> {
FieldsFragment.this.setFocus(fd, false);
return true;
});
fd.editButton.setOnClickListener(v -> openEditor(fd));
binding.fieldsArea.addView(fd);
}
private void updateRowOrder(){
enableSaving();
binding.fieldsArea.removeAllViews();
for(int i = 0; i < views.size(); i++){
binding.fieldsArea.addView(views.get(i));
}
binding.upButton.setEnabled(index != -1 && index > 0);
binding.downButton.setEnabled(index != -1 && index < views.size()-1);
}
private void setFocus(FieldDisplay fd, boolean scroll){
index = views.indexOf(fd);
for(int a = 0; a < values.size(); a++) {
views.get(a).setColor(unfocused_background_color);
views.get(a).hideButtons();
}
fd.setColor(background_color);
fd.showButtons();
binding.upButton.setEnabled(index != -1 && index > 0);
binding.downButton.setEnabled(index != -1 && index < views.size()-1);
if(scroll)
binding.scrollView.post(() -> binding.scrollView.scrollTo(0, fd.getTop()));
}
private void openEditor(FieldDisplay fd){
FieldType field = fd.getField();
ScrollView sv = new ScrollView(getContext());
TableLayout table = new TableLayout(getContext());
table.setStretchAllColumns(true);
table.setPadding(10, 10, 10, 10);
sv.addView(table);
table.addView(new TextViewBuilder(getContext(), "Type: " + field.get_type_name() + "\nUUID: " + field.UUID)
.build());
FieldEditorHelper f = new FieldEditorHelper(getContext(), field, table);
AlertDialog.Builder alert = new AlertDialog.Builder(getContext());
alert.setTitle("Edit " + field.name);
alert.setView(sv);
alert.setCancelable(false);
alert.setNeutralButton("Cancel", (dialogInterface, i) -> {});
alert.setPositiveButton("Save", (dialogInterface, i) -> {
f.save();
fd.setField(field);
enableSaving();
});
AlertDialog dialog = alert.create();
dialog.show();
MaterialButton deleteButton = new MaterialButton(getContext());
deleteButton.setText("DELETE");
deleteButton.setOnClickListener(l -> {
AlertDialog.Builder alert2 = new AlertDialog.Builder(getContext());
alert2.setTitle("Warning!");
alert2.setMessage("This may destroy any data after being saved!");
alert2.setPositiveButton("Return", (dialogInterface, i) -> {});
alert2.setNeutralButton("DELETE", (dialogInterface, i) -> {
removeField(field);
dialog.cancel();
});
alert2.setCancelable(true);
alert2.create().show();
});
table.addView(deleteButton);
}
private void enableSaving(){
edited = true;
binding.saveButton.setEnabled(true);
}
private void addField(int n){
FieldType field = FieldEditorHelper.createNewFieldType(n);
values.add(field);
createFieldDisplay(field);
setFocus(views.get(views.size()-1), true);
enableSaving();
}
private void removeField(FieldType field){
int fieldIndex = values.indexOf(field);
views.remove(fieldIndex);
values.remove(fieldIndex);
index = -1;
updateRowOrder();
}
private void save(){
AlertDialog.Builder alert = new AlertDialog.Builder(getContext());
alert.setTitle("Warning!");
alert.setMessage("Changing or removing some values will result in lost data! but you can revert at any time.");
alert.setNeutralButton("Save", (dialog, which) -> {
FieldType[][] currentValues = Fields.load(filename);
assert currentValues != null;
FieldType[][] newValues = new FieldType[currentValues.length+1][];
System.arraycopy(currentValues, 0, newValues, 0, currentValues.length);
Log.i(getClass().toString(), "Length: " + values.size());
newValues[currentValues.length] = new FieldType[values.size()];
for(int i = 0; i < values.size(); i++) {
FieldType value = values.get(i);
newValues[currentValues.length][i] = value;
}
if(Fields.save(filename, newValues))
AlertManager.toast("Saved");
Navigation.findNavController((Activity) getContext(), R.id.nav_host_fragment_activity_main).navigate(R.id.action_navigation_data_fields_to_navigation_settings);
});
alert.setNegativeButton("Cancel", null);
alert.setCancelable(true);
alert.create().show();
}
public void revertPopup(){
AlertDialog.Builder alert = new AlertDialog.Builder(getContext());
alert.setTitle("Warning!");
alert.setMessage("If there is any data scouted with this version of the fields, it will cause conflicts!\nYou should know what you are doing");
alert.setNeutralButton("Revert and delete version", (dialog, which) -> {
FieldType[][] currentValues = Fields.load(filename);
assert currentValues != null;
FieldType[][] newValues = new FieldType[currentValues.length-1][];
System.arraycopy(currentValues, 0, newValues, 0, currentValues.length - 1);
if(Fields.save(filename, newValues))
AlertManager.toast("Saved");
Navigation.findNavController((Activity) getContext(), R.id.nav_host_fragment_activity_main).navigate(R.id.action_navigation_data_fields_to_navigation_settings);
});
alert.setNegativeButton("Cancel", null);
alert.setCancelable(true);
alert.create().show();
}
}
@@ -0,0 +1,708 @@
package com.ridgebotics.ridgescout.ui.settings;
import static android.widget.LinearLayout.HORIZONTAL;
import static android.widget.LinearLayout.VERTICAL;
import static androidx.navigation.fragment.FragmentKt.findNavController;
import static com.ridgebotics.ridgescout.utility.SettingsManager.AllyPosKey;
import static com.ridgebotics.ridgescout.utility.SettingsManager.CustomEventsKey;
import static com.ridgebotics.ridgescout.utility.SettingsManager.EnableQuickAllianceChangeKey;
import static com.ridgebotics.ridgescout.utility.SettingsManager.FieldImageKey;
import static com.ridgebotics.ridgescout.utility.SettingsManager.MatchNumKey;
import static com.ridgebotics.ridgescout.utility.SettingsManager.SelEVCodeKey;
import static com.ridgebotics.ridgescout.utility.SettingsManager.TeamNumKey;
import static com.ridgebotics.ridgescout.utility.SettingsManager.UnameKey;
import static com.ridgebotics.ridgescout.utility.SettingsManager.WifiModeKey;
import static com.ridgebotics.ridgescout.utility.SettingsManager.YearNumKey;
import static com.ridgebotics.ridgescout.utility.SettingsManager.defaults;
import static com.ridgebotics.ridgescout.utility.SettingsManager.getEVCode;
import static com.ridgebotics.ridgescout.utility.SettingsManager.getEditor;
import static com.ridgebotics.ridgescout.utility.SettingsManager.prefs;
import android.app.AlertDialog;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.checkbox.MaterialCheckBox;
import com.google.android.material.divider.MaterialDivider;
import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;
import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.databinding.FragmentSettingsBinding;
import com.ridgebotics.ridgescout.scoutingData.Fields;
import com.ridgebotics.ridgescout.ui.views.CustomSpinnerView;
import com.ridgebotics.ridgescout.ui.views.TallyCounterView;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.DataManager;
import com.ridgebotics.ridgescout.utility.FileEditor;
import com.ridgebotics.ridgescout.utility.ToDelete;
import com.ridgebotics.ridgescout.utility.builders.TextViewBuilder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
// Fragment to edit settings, aswell as redirect to the fields editor.
public class SettingsFragment extends Fragment {
private FragmentSettingsBinding binding;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
binding = FragmentSettingsBinding.inflate(inflater, container, false);
View root = binding.getRoot();
reloadSettings();
return root;
}
private void reloadSettings(){
String[] alliance_pos_list = new String[]{"red-1", "red-2", "red-3",
"blue-1", "blue-2", "blue-3"};
SettingsManager manager = new SettingsManager(getContext());
ButtonSettingsItem appInfoButton = new ButtonSettingsItem();
appInfoButton.addButton("App info", v -> showAppInfo());
manager.addItem(appInfoButton);
ButtonSettingsItem corruptButton = new ButtonSettingsItem();
corruptButton.addButton("find corrupted files", view -> {
ToDelete.findCorruptedFiles(getContext());
});
corruptButton.addButton("delete files", view -> {
ToDelete.deleteFiles(getContext(), Arrays.asList(FileEditor.getFiles()), false);
});
// corruptButton.setEnabled(!getEVCode().equals("unset"));
manager.addItem(corruptButton);
manager.addItem(new HeaderSettingsItem("Advanced"));
ButtonSettingsItem fieldsButtons = new ButtonSettingsItem();
fieldsButtons.addButton("Edit pit fields", v -> {
FieldsFragment.set_filename(Fields.pitsFieldsFilename);
findNavController(this).navigate(R.id.action_navigation_settings_to_navigation_data_fields);
});
fieldsButtons.addButton("Edit match fields", v -> {
FieldsFragment.set_filename(Fields.matchFieldsFilename);
findNavController(this).navigate(R.id.action_navigation_settings_to_navigation_data_fields);
});
manager.addItem(fieldsButtons);
ButtonSettingsItem noticeButton = new ButtonSettingsItem();
noticeButton.addButton("Edit scout notice", v->editNotice());
noticeButton.setEnabled(!getEVCode().equals("unset"));
manager.addItem(noticeButton);
manager.addItem(new CheckboxSettingsItem(CustomEventsKey, "Custom Events"));
CheckboxSettingsItem FTPSendMetaFiles = new CheckboxSettingsItem(com.ridgebotics.ridgescout.utility.SettingsManager.FTPSendMetaFiles, "[⚠] Send meta files");
manager.addItem(FTPSendMetaFiles);
manager.addItem(new HeaderSettingsItem("Admin"));
StringSettingsItem FTPKey = new StringSettingsItem(com.ridgebotics.ridgescout.utility.SettingsManager.FTPKey, "Sync Key");
manager.addItem(FTPKey);
StringSettingsItem FTPServer = new StringSettingsItem(com.ridgebotics.ridgescout.utility.SettingsManager.FTPServer, "Sync Server (Sync)");
manager.addItem(FTPServer);
CheckboxSettingsItem FTPEnabled = new CheckboxSettingsItem(com.ridgebotics.ridgescout.utility.SettingsManager.FTPEnabled, "Sync Enabled", FTPServer, FTPKey, FTPSendMetaFiles);
manager.addItem(FTPEnabled);
manager.addItem(new CheckboxSettingsItem(WifiModeKey, "Wifi Mode", FTPEnabled));
manager.addItem(new NumberSettingsItem(YearNumKey, "Year", 0, 9999));
manager.addItem(new HeaderSettingsItem("Connection"));
manager.addItem(new CheckboxSettingsItem(EnableQuickAllianceChangeKey, "Enable quick alliance swap", null));
manager.addItem(new CheckboxSettingsItem(FieldImageKey, "Field Image", null));
manager.addItem(new DropdownSettingsItem(AllyPosKey, "Alliance Pos", alliance_pos_list));
int max = 0;
boolean hasEvent = false;
if(!DataManager.getevcode().equals("unset")){
DataManager.reload_event();
max = DataManager.event.matches.size();
hasEvent = true;
}
TallySettingsItem matchNum = new TallySettingsItem(MatchNumKey, "Match Number", max);
matchNum.setEnabled(hasEvent);
manager.addItem(matchNum);
DropdownSettingsItem eventCode = new DropdownSettingsItem(SelEVCodeKey, "Event Code", FileEditor.getEventList().toArray(new String[0]));
eventCode.reloadOnChange(true);
manager.addItem(eventCode);
manager.addItem(new StringSettingsItem(UnameKey, "Username"));
manager.addItem(new NumberSettingsItem(TeamNumKey, "Team Number", 0, 99999));
manager.addItem(new HeaderSettingsItem("Scouting"));
binding.SettingsTable.removeAllViews();
manager.getView(binding.SettingsTable);
// Add "Edit scout notice" button to the bottom of the page=
// Add "Field edit" buttons to the bottom of the page
// LinearLayout fieldButtons = new LinearLayout(getContext());
// fieldButtons.setLayoutParams(new LinearLayout.LayoutParams(
// LinearLayout.LayoutParams.MATCH_PARENT,
// LinearLayout.LayoutParams.WRAP_CONTENT
// ));
// fieldButtons.setOrientation(HORIZONTAL);
//
// Button editMatchFieldsButton = new Button(getContext());
// editMatchFieldsButton.setText("Edit Match Fields");
// editMatchFieldsButton.setOnClickListener(v -> {
// FieldsFragment.set_filename(Fields.matchFieldsFilename);
// findNavController(this).navigate(R.id.action_navigation_settings_to_navigation_data_fields);
// });
// fieldButtons.addView(editMatchFieldsButton);
//
//
// Button editPitsFieldsButton = new Button(getContext());
// editPitsFieldsButton.setText("Edit pits Fields");
// editPitsFieldsButton.setOnClickListener(v -> {
// FieldsFragment.set_filename(Fields.pitsFieldsFilename);
// findNavController(this).navigate(R.id.action_navigation_settings_to_navigation_data_fields);
// });
// fieldButtons.addView(editPitsFieldsButton);
// binding.SettingsTable.addView(fieldButtons);
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
private void editNotice(){
ScrollView sv = new ScrollView(getContext());
EditText editText = new EditText(getContext());
editText.setText(DataManager.scoutNotice);
sv.addView(editText);
AlertDialog.Builder alert = new AlertDialog.Builder(getContext());
alert.setTitle("Edit Notice");
alert.setView(sv);
alert.setNeutralButton("Cancel", null);
alert.setPositiveButton("Save", (dialogInterface, i) -> {
DataManager.scoutNotice = editText.getText().toString();
DataManager.save_scout_notice();
});
alert.setCancelable(false);
alert.create().show();
}
private TextView createText(String title) {
return new TextViewBuilder(getContext(), title)
.body1().build();
}
private void showAppInfo() {
LinearLayout ll = new LinearLayout(getContext());
ll.setOrientation(VERTICAL);
ll.setPadding(10, 10, 10, 10);
try {
PackageInfo pInfo = getContext().getPackageManager().getPackageInfo(getContext().getPackageName(), 0);
ll.addView(createText("Package: " + pInfo.packageName));
ll.addView(createText("Version: " + pInfo.versionName));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
ll.addView(createText("Signature: " + (pInfo.signingInfo != null ? "True" : "False")));
}
} catch (PackageManager.NameNotFoundException e) {
AlertManager.error("Failed to get version info", e);
}
AlertDialog.Builder alert = new AlertDialog.Builder(getContext());
alert.setTitle("App info");
alert.setView(ll);
alert.setNeutralButton("Ok", null);
alert.setCancelable(true);
alert.create().show();
}
public abstract class SettingsItem<T> {
private String key;
private String title;
private T defaultValue;
public View view;
public SettingsItem(String key, String title, T defaultValue) {
this.key = key;
this.title = title;
this.defaultValue = defaultValue;
}
private boolean reloadOnChange = false;
public void reloadOnChange(boolean enabled){
reloadOnChange = enabled;
}
public boolean isReloadOnChange(){
return reloadOnChange;
}
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();
if(isReloadOnChange()) reloadSettings();
}
@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 TextViewBuilder(context, getTitle())
.sub1().build();
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()));
}
if(isReloadOnChange()) reloadSettings();
}
@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 TallySettingsItem extends SettingsItem<Integer> {
private int max;
public TallySettingsItem(String key, String title, int max) {
super(key, title, prefs.getInt(key, (int) defaults.get(key)));
this.max = max;
}
TallyCounterView tally;
private boolean enabled;
@Override
public void setEnabled(boolean enabled){
this.enabled = enabled;
if(tally != null)
tally.setEnabled(enabled);
}
@Override
public View createView(Context context) {
LinearLayout ll = new LinearLayout(getContext());
ll.setOrientation(VERTICAL);
tally = new TallyCounterView(getContext());
int value = getValue()+1;
if(value >= max){
value = max;
getEditor().putInt(getKey(), Math.max(0,max-1)).apply();
}
tally.setValue(value);
tally.setBounds(1, max);
tally.setOnCountChangedListener(count -> {
getEditor().putInt(getKey(), Math.max(0,count-1)).apply();
if(isReloadOnChange()) reloadSettings();
});
tally.setEnabled(enabled);
ll.addView(new TextViewBuilder(getContext(), getTitle())
.h6()
.build());
ll.addView(tally);
return ll;
}
@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();
if(isReloadOnChange()) reloadSettings();
});
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, @Nullable SettingsItem<?>... controlledItems) {
super(key, title, prefs.getBoolean(key, (Boolean) defaults.get(key)));
this.controlledItems = (controlledItems != null) ? Arrays.asList(controlledItems) : new ArrayList<>();
}
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);
}
if(isReloadOnChange()) reloadSettings();
});
for (SettingsItem<?> item : controlledItems) {
item.setEnabled(getValue());
}
return checkBox;
}
@Override
public Boolean getValue() {
return prefs.getBoolean(getKey(), (Boolean) defaults.get(getKey()));
}
}
public class HeaderSettingsItem extends SettingsItem<Void> {
String title;
public HeaderSettingsItem(String title) {
super("", title, null);
this.title = title;
}
@Override
public void setEnabled(boolean enabled){
}
@Override
public View createView(Context context) {
LinearLayout ll = new LinearLayout(context);
ll.setOrientation(VERTICAL);
ll.setPadding(0, 20,0,0);
ll.addView(new TextViewBuilder(context, title)
.h4()
.build());
ll.addView(new MaterialDivider(context));
return ll;
}
@Override
public Void getValue() {
return null;
}
}
public class ButtonSettingsItem extends SettingsItem<Void> {
List<MaterialButton> buttons = new ArrayList<>();
List<String> titles = new ArrayList<>();
List<View.OnClickListener> callbacks = new ArrayList<>();
boolean enabled = true;
public ButtonSettingsItem() {
super("", "", null);
}
@Override
public void setEnabled(boolean enabled){
this.enabled = enabled;
for(int i = 0; i < buttons.size(); i++){
buttons.get(i).setEnabled(enabled);
}
}
public void addButton(String text, View.OnClickListener onClickListener) {
titles.add(text);
callbacks.add(onClickListener);
}
@Override
public View createView(Context context) {
LinearLayout ll = new LinearLayout(context);
ll.setOrientation(HORIZONTAL);
for(int i = 0; i < titles.size(); i++){
MaterialButton button = new MaterialButton(context);
button.setText(titles.get(i));
button.setOnClickListener(callbacks.get(i));
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT, 1.0f);
layoutParams.setMargins(3, 0, 3, 0);
// layoutParams.weight
button.setLayoutParams(layoutParams);
button.setEnabled(enabled);
// button.weight
buttons.add(button);
ll.addView(button);
}
return ll;
}
@Override
public Void getValue() {
return null;
}
}
public class SettingsManager {
private Context context;
private HashMap<String, Object> settings;
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(VERTICAL);
itemContainer.setPadding(32, 0, 32, 8);
View view = item.createView(context);
itemContainer.addView(view);
item.view = view;
views.add(itemContainer);
}
public void getView(LinearLayout layout) {
for(int i = views.size()-1; i >= 0; i--)
layout.addView(views.get(i));
}
}
}
@@ -1,238 +0,0 @@
package com.ridgebotics.ridgescout.ui.settings;
import android.app.AlertDialog;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.Spinner;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.databinding.FragmentSettingsBinding;
import com.ridgebotics.ridgescout.types.data.intType;
import com.ridgebotics.ridgescout.utility.fileEditor;
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.List;
public class settingsFragment extends Fragment {
private FragmentSettingsBinding binding;
private android.widget.ScrollView ScrollArea;
private android.widget.TableLayout Table;
private void setDropdownItems(Spinner dropdown, String[] items){
ArrayAdapter<String> adapter = new ArrayAdapter<>(requireActivity(), android.R.layout.simple_spinner_item, items);
dropdown.setAdapter(adapter);
}
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,
ViewGroup container, Bundle savedInstanceState) {
binding = FragmentSettingsBinding.inflate(inflater, container, false);
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",
"blue-1", "blue-2", "blue-3"};
for(int i = 0; i < alliance_pos_list.length; i++){
if(alliance_pos_list[i].equals(target_alliance_pos)){
alliance_pos_target_index = i;
}
alliance_pos_iconSpinnerItems.add(new IconSpinnerItem(alliance_pos_list[i]));
}
IconSpinnerAdapter alliance_pos_iconSpinnerAdapter = new IconSpinnerAdapter(alliance_pos_spinnerView);
alliance_pos_spinnerView.setSpinnerAdapter(alliance_pos_iconSpinnerAdapter);
alliance_pos_spinnerView.setItems(alliance_pos_iconSpinnerItems);
alliance_pos_spinnerView.setLifecycleOwner(this);
if(alliance_pos_target_index != -1){
alliance_pos_spinnerView.selectItemByIndex(alliance_pos_target_index);
}
alliance_pos_spinnerView.setOnSpinnerItemSelectedListener(new OnSpinnerItemSelectedListener<IconSpinnerItem>() {
@Override
public void onItemSelected(int oldIndex, @Nullable IconSpinnerItem oldItem, int newIndex,
IconSpinnerItem newItem) {
settingsManager.setAllyPos(newItem.getText().toString());
}
});
//
// CheckBox practice_mode = binding.practiceMode;
// practice_mode.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
// @Override
// public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
// latestSettings.settings.set_practice_mode(isChecked);
// }
//
// });
//
// practice_mode.setChecked(latestSettings.settings.get_practice_mode());
EditText team_num = binding.teamNumber;
team_num.setText(String.valueOf(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;
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}
@@ -9,15 +9,20 @@ import static com.ridgebotics.ridgescout.utility.SharePrompt.shareContent;
import android.content.Context; import android.content.Context;
import com.ridgebotics.ridgescout.scoutingData.ScoutingDataWriter; import com.ridgebotics.ridgescout.scoutingData.ScoutingDataWriter;
import com.ridgebotics.ridgescout.types.data.dataType; import com.ridgebotics.ridgescout.types.data.RawDataType;
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.FieldType;
import com.ridgebotics.ridgescout.utility.DataManager; import com.ridgebotics.ridgescout.utility.DataManager;
import com.ridgebotics.ridgescout.utility.fileEditor; import com.ridgebotics.ridgescout.utility.FileEditor;
// Static class to export matches to a string.
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();
@@ -52,13 +57,21 @@ public class CSVExport {
data += (teamNum + ","); data += (teamNum + ",");
String filename = evcode+"-"+matchNum+"-"+alliance+"-"+alliancePos+"-"+teamNum+".matchscoutdata"; String filename = evcode+"-"+matchNum+"-"+alliance+"-"+alliancePos+"-"+teamNum+".matchscoutdata";
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; RawDataType[] matchData = psdr.data.array;
FieldType[] 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(match_latest_values.length));
} }
} }
@@ -77,7 +90,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 + ",");
} }
@@ -96,13 +109,21 @@ public class CSVExport {
data += (team.startingYear + ","); data += (team.startingYear + ",");
String filename = evcode+"-"+team.teamNumber+".pitscoutdata"; String filename = evcode+"-"+team.teamNumber+".pitscoutdata";
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; RawDataType[] teamData = psdr.data.array;
FieldType[] 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));
} }
} }
@@ -6,13 +6,13 @@ import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import com.ridgebotics.ridgescout.MainActivity; import com.ridgebotics.ridgescout.MainActivity;
import com.ridgebotics.ridgescout.types.file; import com.ridgebotics.ridgescout.types.ScoutingFile;
import com.ridgebotics.ridgescout.utility.AlertManager; import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.BuiltByteParser; import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.ByteBuilder; import com.ridgebotics.ridgescout.utility.ByteBuilder;
import com.ridgebotics.ridgescout.utility.DataManager; import com.ridgebotics.ridgescout.utility.DataManager;
import com.ridgebotics.ridgescout.utility.SharePrompt; import com.ridgebotics.ridgescout.utility.SharePrompt;
import com.ridgebotics.ridgescout.utility.fileEditor; import com.ridgebotics.ridgescout.utility.FileEditor;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
@@ -20,28 +20,8 @@ import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
// Class to create the share and receive popups to transfer scouting data.
public class FileBundle { public class FileBundle {
private static final Intent FILE_SELECT_CODE = new Intent();
public static void send(String[] files, Context c){
try {
ByteBuilder b = new ByteBuilder();
for(int i = 0; i < files.length; i++){
if(!fileEditor.fileExist(files[i])) continue;
// byte[] data = fileEditor.readFile(files[i]);
file f = new file(files[i]);
b.addRaw(file.typecode, f.encode());
}
byte[] data = b.build();
send(data, c);
} catch (ByteBuilder.buildingException e) {
AlertManager.error(e);
}
}
public static void send(byte[] data, Context c){ public static void send(byte[] data, Context c){
String filename = DataManager.getevcode() + "-" + System.currentTimeMillis() + ".scoutbundle"; String filename = DataManager.getevcode() + "-" + System.currentTimeMillis() + ".scoutbundle";
SharePrompt.shareContent(c, filename, data, "application/ridgescout"); SharePrompt.shareContent(c, filename, data, "application/ridgescout");
@@ -58,6 +38,7 @@ public class FileBundle {
MainActivity.setResultRelay(new MainActivity.activityResultRelay() { MainActivity.setResultRelay(new MainActivity.activityResultRelay() {
@Override @Override
public void onActivityResult(int requestCode, int resultCode, Intent data) { public void onActivityResult(int requestCode, int resultCode, Intent data) {
if(data == null) return;
Uri uri = data.getData(); Uri uri = data.getData();
if(uri == null) return; if(uri == null) return;
@@ -90,18 +71,18 @@ public class FileBundle {
for(int i = 0; i < parsedObjectList.size(); i++){ for(int i = 0; i < parsedObjectList.size(); i++){
BuiltByteParser.parsedObject pa = parsedObjectList.get(i); BuiltByteParser.parsedObject pa = parsedObjectList.get(i);
if(pa.getType() != file.typecode) continue; if(pa.getType() != ScoutingFile.typecode) continue;
file f = file.decode((byte[]) pa.get()); ScoutingFile f = ScoutingFile.decode((byte[]) pa.get());
if(f == null) continue; if(f == null) continue;
filenames.add(f.filename); filenames.add(f.filename);
fileEditor.writeFile(f.filename, f.data); FileEditor.writeFile(f.filename, f.data);
} }
AlertManager.alert("Saved", AlertManager.alert("Saved",
String.join("\n", filenames)); String.join("\n", filenames));
}catch (BuiltByteParser.byteParsingExeption e){ }catch (Exception e){
AlertManager.error(e); AlertManager.error("Failed saving files!", e);
} }
} }
} }
@@ -1,11 +1,14 @@
package com.ridgebotics.ridgescout.ui.transfer; package com.ridgebotics.ridgescout.ui.transfer;
import static com.ridgebotics.ridgescout.utility.Colors.fileselector_selected_color;
import static com.ridgebotics.ridgescout.utility.Colors.fileselector_unselected_color;
import static com.ridgebotics.ridgescout.utility.DataManager.evcode; import static com.ridgebotics.ridgescout.utility.DataManager.evcode;
import android.os.Bundle; 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;
@@ -16,18 +19,19 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.databinding.FragmentTransferFileSelectorBinding; import com.ridgebotics.ridgescout.databinding.FragmentTransferFileSelectorBinding;
import com.ridgebotics.ridgescout.types.file; import com.ridgebotics.ridgescout.types.ScoutingFile;
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.fileEditor; import com.ridgebotics.ridgescout.utility.DataManager;
import com.ridgebotics.ridgescout.utility.FileEditor;
import com.ridgebotics.ridgescout.utility.builders.TextViewBuilder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
// Class to show a file browseer
public class FileSelectorFragment extends Fragment { public class FileSelectorFragment extends Fragment {
private static final int background_color = 0x5000ff00;
private static final int unselected_background_color = 0x2000ff00;
private static on_file_select onSelect = files -> {}; private static on_file_select onSelect = files -> {};
@@ -41,14 +45,18 @@ 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",
"pits.fields", "pits.fields",
evcode+".eventdata" evcode+".eventdata",
evcode+".rescout",
evcode+".scoutnotice",
"todelete.colabarray",
}; };
String[] files = fileEditor.getEventFiles(evcode); String[] files = FileEditor.getEventFiles(evcode);
Boolean[] selected_arr = new Boolean[files.length]; Boolean[] selected_arr = new Boolean[files.length];
Arrays.fill(selected_arr, Boolean.TRUE); Arrays.fill(selected_arr, Boolean.TRUE);
@@ -64,19 +72,33 @@ public class FileSelectorFragment extends Fragment {
tr.setPadding(20,20,20,20); tr.setPadding(20,20,20,20);
binding.fileSelectorTable.addView(tr); binding.fileSelectorTable.addView(tr);
tr.setBackgroundColor(background_color); tr.setBackgroundColor(fileselector_selected_color);
TextView tv = new TextView(getContext()); CheckBox checkBox = new CheckBox(getContext());
tv.setText(String.valueOf(files[i])); checkBox.setChecked(true);
tv.setTextSize(20); tr.addView(checkBox);
tr.addView(tv);
// Filename
tr.addView(
new TextViewBuilder(getContext(), files[i])
.size(20)
.build()
);
final int fi = i; final int fi = i;
tr.setOnClickListener(view -> { tr.setOnClickListener(view -> {
boolean sel = !selected_arr[fi]; boolean sel = !selected_arr[fi];
selected_arr[fi] = sel; selected_arr[fi] = sel;
tr.setBackgroundColor(sel ? background_color : unselected_background_color); tr.setBackgroundColor(sel ? fileselector_selected_color : fileselector_unselected_color);
((CheckBox) tr.getChildAt(0)).setChecked(sel);
});
checkBox.setOnClickListener(view -> {
boolean sel = !selected_arr[fi];
selected_arr[fi] = sel;
tr.setBackgroundColor(sel ? fileselector_selected_color : fileselector_unselected_color);
((CheckBox) tr.getChildAt(0)).setChecked(sel);
}); });
} }
@@ -88,9 +110,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(fileselector_selected_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 +128,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));
}); });
@@ -176,8 +202,21 @@ public class FileSelectorFragment extends Fragment {
ByteBuilder b = new ByteBuilder(); ByteBuilder b = new ByteBuilder();
for(int i = 0; i < filenames.size(); i++){ for(int i = 0; i < filenames.size(); i++){
file f = new file(filenames.get(i)); ScoutingFile f = new ScoutingFile(filenames.get(i));
b.addRaw(file.typecode, f.encode());
if(!FileEditor.fileExist(f.filename)) {
AlertManager.addSimpleError("File " + f.filename + " Does not exist!");
continue;
};
byte[] bytes = f.encode();
if(bytes == null || bytes.length == 0) {
AlertManager.addSimpleError("File " + f.filename + " Has no data!");
continue;
};
b.addRaw(ScoutingFile.typecode, bytes);
} }
return b.build(); return b.build();
@@ -0,0 +1,385 @@
package com.ridgebotics.ridgescout.ui.transfer;
import static com.ridgebotics.ridgescout.utility.FileEditor.baseDir;
import android.util.Log;
import com.ridgebotics.ridgescout.types.ColabArray;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.FileEditor;
import com.ridgebotics.ridgescout.utility.HttpGetFile;
import com.ridgebotics.ridgescout.utility.HttpPutFile;
import com.ridgebotics.ridgescout.utility.RequestTask;
import com.ridgebotics.ridgescout.utility.SettingsManager;
import com.ridgebotics.ridgescout.utility.ToDelete;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
// Class to synchronise data over HTTP.
public class HttpSync extends Thread {
public static final String timestampsFilename = "timestamps";
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();
HttpSync sync = new HttpSync();
sync.start();
}
private int upCount = 0;
private int downCount = 0;
private class TransferFile {
public String filename;
public Date updated;
public String checksum;
}
private List<TransferFile> localFiles = new ArrayList<>();
private List<TransferFile> remoteFiles = new ArrayList<>();
private void await() {
while(!runningRequest.get()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {}
}
}
AtomicBoolean runningRequest = new AtomicBoolean(false);
public void run() {
isRunning = true;
boolean sendMetaFiles = SettingsManager.getFTPSendMetaFiles();
ToDelete.reload_todelete_list();
List<String> removeFiles = ToDelete.todelete_list.get();
String serverIP = SettingsManager.getFTPServer();
String serverKey = SettingsManager.getFTPKey();
setUpdateIndicator("Getting Metadata...");
// Load metadata from server
getRemoteFileMetadata(serverIP, serverKey);
if(!isRunning){
setUpdateIndicator("Error Connecting");
onResult.onResult(true, upCount, downCount);
return;
}
getLocalFileMetadata();
localFiles.removeIf(localFile -> removeFiles.contains(localFile.filename+","+localFile.checksum));
remoteFiles.removeIf(remoteFile -> removeFiles.contains(remoteFile.filename+","+remoteFile.checksum));
// Wait for metadata request to finish
setUpdateIndicator("Uploading 0%");
for(int i = 0; i < localFiles.size(); i++){
TransferFile localFile = localFiles.get(i);
TransferFile remoteFile = findInFileArray(remoteFiles, localFile.filename);
// Check if the file is a meta file, and uploads it based off of the setting
boolean sendField = (sendMetaFiles || !(localFile.filename.endsWith(".fields")));
boolean shouldUpload;
boolean special;
// If there is no file on the sever, upload.
if(remoteFile == null) {
shouldUpload = true;
special = false;
}
else {
// If the remote file is the same as the local one, do nothing.
boolean checksumsEqual = Objects.equals(localFile.checksum, remoteFile.checksum);
// If the local file is a colabarray, give it a special propreties
special = FileEditor.requiresSpecialInteraction(remoteFile.filename);
// If the local file is updated after the remote file
boolean after = after(localFile.updated, remoteFile.updated);
shouldUpload = !checksumsEqual && (special || after);
}
if(sendField && shouldUpload) {
uploadFile(localFile, serverIP, serverKey, special);
Log.d(getClass().toString(), "LocalFile: " + localFile.filename + ", " + localFile.checksum + ", " + localFile.updated + ": Uploaded");
upCount++;
}else {
Log.d(getClass().toString(), "LocalFile: " + localFile.filename + ", " + localFile.checksum + ", " + localFile.updated + ": Not uploaded");
}
setUpdateIndicator("Uploading " + (Math.floor((double) (i * 1000) / localFiles.size()) / 10) + "%");
}
setUpdateIndicator("Downloading 0%");
for(int i = 0; i < remoteFiles.size(); i++){
TransferFile remoteFile = remoteFiles.get(i);
TransferFile localFile = findInFileArray(localFiles, remoteFile.filename);
boolean shouldUpload;
// If there is no file on the sever, upload.
if(localFile == null) {
shouldUpload = true;
} else {
// If the remote file is the same as the local one, do nothing.
boolean checksumsEqual = !Objects.equals(localFile.checksum, remoteFile.checksum);
// If the local file is updated after the remote file
boolean after = after(remoteFile.updated, localFile.updated);
// If the local file and remote file's upload dates are exactly the same
boolean datesEqual = !localFile.updated.equals(remoteFile.updated);
shouldUpload = (!checksumsEqual && (after) && !datesEqual);
}
if(shouldUpload) {
downloadFile(remoteFile, serverIP);
// await();
Log.d(getClass().toString(), "RemoteFile: " + remoteFile.filename + ", " + remoteFile.checksum + ", " + remoteFile.updated + ": Downloaded");
downCount++;
} else {
Log.d(getClass().toString(), "RemoteFile: " + remoteFile.filename + ", " + remoteFile.checksum + ", " + remoteFile.updated + ": Not downloaded");
}
setUpdateIndicator("Downloading " + (Math.floor((double) (i * 1000) / remoteFiles.size()) / 10) + "%");
}
// Remove files marked for deletion
ToDelete.deleteFiles();
setUpdateIndicator("Finished, " + upCount + " Up, " + downCount + " Down");
onResult.onResult(false, upCount, downCount);
isRunning = false;
}
// Find file based off of filename
private TransferFile findInFileArray(List<TransferFile> files, String filename){
for(TransferFile file : files) {
if(file.filename.equals(filename))
return file;
}
return null;
}
// Get teh last modified date of a file
private Date getLocalFileUtcTimestamp(File file) {
return new Date(file.lastModified());
}
// Load the local metadata of files
private void getLocalFileMetadata() {
File localDir = new File(baseDir);
File[] localFileNames = localDir.listFiles();
assert localFileNames != null;
for (int i = 0; i < localFileNames.length; i++) {
File file = localFileNames[i];
if(file.isDirectory()) continue;
// Remove timestamts file
if(file.getName().equals(timestampsFilename)) continue;
TransferFile tf = new TransferFile();
tf.filename = file.getName();
tf.updated = getLocalFileUtcTimestamp(file);
try {
tf.checksum = FileEditor.getSHA256Hash(file.getName());
} catch (Exception e) {
AlertManager.error("Failed to get hash of: " + file.getName(), e);
continue;
}
localFiles.add(tf);
}
}
// Send request to server and retrieve metadata
private void getRemoteFileMetadata(String serverURL, String serverKey) {
final RequestTask rq = new RequestTask();
runningRequest.set(false);
rq.onResult(metadata -> {
try {
JSONObject j = new JSONObject(metadata);
for (Iterator<String> it = j.keys(); it.hasNext(); ) {
String key = it.next();
JSONObject obj = j.getJSONObject(key);
TransferFile tf = new TransferFile();
tf.filename = key;
tf.updated = new Date(Long.parseLong(obj.getString("modified")));
tf.checksum = obj.getString("sha256");
remoteFiles.add(tf);
}
}catch(JSONException | NullPointerException e ) {
AlertManager.error(e);
isRunning = false;
}
runningRequest.set(true);
return null;
});
rq.execute((serverURL + "/api/metadata"), "api_key: " + serverKey);
await();
}
// Create HTTP request to upload file
void uploadFile(TransferFile tf, String serverURL, String apiKey, boolean special) {
runningRequest.set(false);
// If the file is "special", download the server copy and merge the local and remote ColabArrays
if(special) {
HttpGetFile getTask = new HttpGetFile(serverURL + "/api/" + tf.filename, new File(baseDir + tf.filename), (stream, error) -> {
if(error != null) {
AlertManager.error(error);
return;
} else if (stream == null) {
AlertManager.error("Output stream from download was null!");
return;
}
byte[] bytes = stream.toByteArray();
FileEditor.syncColabArray(
tf.filename,
FileEditor.readFile(tf.filename),
bytes
);
HttpPutFile uploadTask = new HttpPutFile(serverURL + "/api/" + tf.filename, new File(baseDir + tf.filename), error2 -> {
if (error2 != null)
AlertManager.error(error2);
runningRequest.set(true);
}, new String[]{
"api_key: " + apiKey,
("modified: " + tf.updated.getTime())
});
uploadTask.execute();
});
getTask.execute();
} else {
// Upload the file
HttpPutFile uploadTask = new HttpPutFile(serverURL + "/api/" + tf.filename, new File(baseDir + tf.filename), error -> {
if (error != null)
AlertManager.error(error);
runningRequest.set(true);
}, new String[]{
"api_key: " + apiKey,
("modified: " + tf.updated.getTime())
}); // Pass auth token if needed
uploadTask.execute();
await();
}
}
private void setLocalFileTimestamp(File file, Date date) {
file.setLastModified(date.getTime());
}
// Download a file from the remote server
void downloadFile(TransferFile tf, String serverURL) {
runningRequest.set(false);
File f = new File(baseDir + tf.filename);
HttpGetFile uploadTask = new HttpGetFile(serverURL + "/api/" + tf.filename, f, (stream, error) -> {
if(error != null) {
AlertManager.error(error);
return;
} else if (stream == null) {
AlertManager.error("Output stream from download was null!");
return;
}
byte[] bytes = stream.toByteArray();
if(FileEditor.requiresSpecialInteraction(tf.filename)) {
FileEditor.syncColabArray(
tf.filename,
FileEditor.readFile(tf.filename),
bytes
);
} else {
FileEditor.writeFile(tf.filename, bytes);
}
setLocalFileTimestamp(f, tf.updated);
runningRequest.set(true);
});
uploadTask.execute();
await();
}
}
@@ -0,0 +1,413 @@
package com.ridgebotics.ridgescout.ui.transfer;
import static androidx.navigation.fragment.FragmentKt.findNavController;
import static com.ridgebotics.ridgescout.utility.Colors.tba_blue;
import static com.ridgebotics.ridgescout.utility.Colors.tba_red;
import static com.ridgebotics.ridgescout.utility.Colors.tba_toggle_background;
import static com.ridgebotics.ridgescout.utility.FileEditor.TBAAddress;
import static com.ridgebotics.ridgescout.utility.FileEditor.TBAHeader;
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.util.Base64;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.google.android.material.button.MaterialButton;
import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.databinding.FragmentTransferTbaBinding;
import com.ridgebotics.ridgescout.types.frcEvent;
import com.ridgebotics.ridgescout.types.frcMatch;
import com.ridgebotics.ridgescout.types.frcTeam;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.ImageRequestTask;
import com.ridgebotics.ridgescout.utility.JSONUtil;
import com.ridgebotics.ridgescout.utility.RequestTask;
import com.ridgebotics.ridgescout.utility.FileEditor;
import com.ridgebotics.ridgescout.utility.SettingsManager;
import com.ridgebotics.ridgescout.utility.builders.TextViewBuilder;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.function.Function;
// Class to download data from a specific event and encode it.
public class TBAEventFragment extends Fragment {
private TableLayout Table;
private FragmentTransferTbaBinding binding;
private final int year = SettingsManager.getYearNum();
private static JSONObject eventData = null;
public static void setEventData(JSONObject j){
eventData = j;
}
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentTransferTbaBinding.inflate(inflater, container, false);
final String matchKey;
try {
matchKey = eventData.getString("key");
} catch (JSONException e) {
AlertManager.error("Failed loading event key!", e);
return binding.getRoot();
}
Table = binding.matchTable;
AlertManager.startLoading("Loading Teams and Matches...");
// Table.removeAllViews();
Table.setStretchAllColumns(true);
Table.bringToFront();
final RequestTask rq = new RequestTask();
rq.onResult(teamsStr -> {
final RequestTask rq1 = new RequestTask();
rq1.onResult(matchesStr -> {
matchTable(matchesStr, teamsStr, eventData);
AlertManager.stopLoading();
return null;
});
rq1.execute((TBAAddress + "event/" + matchKey + "/matches"), TBAHeader);
return null;
});
rq.execute((TBAAddress + "event/" + matchKey + "/teams"), TBAHeader);
return binding.getRoot();
}
private void addTableText(TableRow tr, String textStr){
tr.addView(new TextViewBuilder(getContext(), textStr)
.size(18)
// .align_center()
.build());
}
public void matchTable(String matchesString, String teamsString, JSONObject eventData){
try {
final JSONArray matchData = new JSONArray(matchesString);
// final JSONArray matchData = new JSONArray();
final JSONArray teamData = new JSONArray(teamsString);
String matchKey = eventData.getString("key");
String matchName = eventData.getString("short_name");
// Sometimes, a short name is not present on TBA Events
if(matchName.isEmpty()){
matchName = eventData.getString("name");
}
// Event code at top
Table.addView(new TextViewBuilder(getContext(), matchKey)
.align_center()
.size(18)
.build());
// Event Name
Table.addView(new TextViewBuilder(getContext(), matchName)
.align_center()
.size(28)
.build());
// Save button
MaterialButton btn = new MaterialButton(getContext());
btn.setText("Save");
btn.setTextSize(18);
btn.setLayoutParams(new TableRow.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
Table.addView(btn);
// If there are no matches, add the error.
// If there are no teams, don't allow the user to save the event and set the button to be invisible
if(teamData.length() == 0){
Table.addView(new TextViewBuilder(getContext(), "This event has no teams released yet...")
.align_center()
.size(18)
.build());
btn.setVisibility(View.GONE);
return;
}else if(matchData.length() == 0){
Table.addView(new TextViewBuilder(getContext(), "This event has no matches released yet...")
.align_center()
.size(18)
.build());
Table.addView(new TextViewBuilder(getContext(), "Try manually adding practice matches.")
.align_center()
.size(18)
.build());
}
Table.addView(
new TextViewBuilder(getContext(), "Teams")
.align_center()
.size(28)
.build()
);
// Sort the teams into numerical order
int[] teams = new int[teamData.length()];
for(int i = 0 ; i < teamData.length(); i++){
teams[i] = teamData.getJSONObject(i).getInt("team_number");
}
Arrays.sort(teams);
// Loop through each match
TableRow tr = null;
for(int i=0; i < teamData.length(); i++){
int num = teams[i];
// If this is every 7th row, add the new row.
if(i % 7 == 0){
if(i != 0)
Table.addView(tr);
tr = new TableRow(getContext());
}
tr.addView(
new TextViewBuilder(getContext(), String.valueOf(num))
.align_center()
.size(18)
.build()
);
}
if(tr != null)
Table.addView(tr);
final ArrayList<frcMatch> matchesOBJ = new ArrayList<>();
btn.setOnClickListener(v -> {
saveData(matchesOBJ, teamData, eventData);
});
Table.addView(
new TextViewBuilder(getContext(), "Matches")
.align_center()
.size(28)
.build()
);
tr = new TableRow(getContext());
addTableText(tr, "#");
addTableText(tr, "Red-1");
addTableText(tr, "Red-2");
addTableText(tr, "Red-3");
addTableText(tr, "Blue-1");
addTableText(tr, "Blue-2");
addTableText(tr, "Blue-3");
Table.addView(tr);
if(matchData.length() == 0)
return;
final JSONArray sortedMatchData = JSONUtil.sort(matchData, (a, b) -> {
JSONObject ja = (JSONObject)a;
JSONObject jb = (JSONObject)b;
try {
return ja.getInt("match_number") - jb.getInt("match_number");
}catch (JSONException j){
return 0;
}
});
boolean toggle = false;
int matchCount = 1;
for(int a=0;a<sortedMatchData.length();a++){
final JSONObject match = sortedMatchData.getJSONObject(a);
if(!match.getString("comp_level").equals("qm")){
continue;
}
final JSONObject alliances = match.getJSONObject("alliances");
final JSONArray redAlliance = alliances.getJSONObject("red").getJSONArray("team_keys");
final JSONArray blueAlliance = alliances.getJSONObject("blue").getJSONArray("team_keys");
tr = new TableRow(getContext());
if (toggle) {
tr.setBackgroundColor(tba_toggle_background);
}
addTableText(tr, String.valueOf(matchCount));
// addTableText(tr, match.getString("key"));
int[] blueKeys = new int[3];
int[] redKeys = new int[3];
for(int b=0;b<6;b++){
TextViewBuilder text = new TextViewBuilder(getContext())
.size(18)
.align_center();
if(b < 3){
String str = redAlliance.getString(b).substring(3);
redKeys[b] = Integer.parseInt(str);
text.text(str);
text.tv.setBackgroundColor(tba_red);
}else{
String str = blueAlliance.getString(b-3).substring(3);
blueKeys[b-3] = Integer.parseInt(str);
text.text(str);
text.tv.setBackgroundColor(tba_blue);
}
tr.addView(text.build());
}
Table.addView(tr);
frcMatch matchOBJ = new frcMatch();
matchOBJ.matchIndex = matchCount;
matchOBJ.blueAlliance = blueKeys;
matchOBJ.redAlliance = redKeys;
matchesOBJ.add(matchOBJ);
matchCount += 1;
toggle = !toggle;
}
}catch (JSONException j){
AlertManager.error("Failed Downloading", j);
AlertManager.stopLoading();
}
}
private boolean saveData(ArrayList<frcMatch> matchData, JSONArray teamData, JSONObject eventData){
AlertManager.startLoading("Downloading team data...");
Thread t = new Thread(() -> {
try {
final String matchKey = eventData.getString("key");
String matchName = eventData.getString("short_name");
// Sometimes, a short name is not present on TBA Events
if (matchName.isEmpty()) {
matchName = eventData.getString("name");
}
ArrayList<frcTeam> teams = new ArrayList<>();
for (int i = 0; i < teamData.length(); i++) {
frcTeam teamObj = new frcTeam();
JSONObject team = teamData.getJSONObject(i);
teamObj.teamNumber = team.getInt("team_number");
teamObj.teamName = team.getString("nickname");
teamObj.city = team.getString("city");
teamObj.stateOrProv = team.getString("state_prov");
teamObj.school = team.getString("school_name");
teamObj.country = team.getString("country");
teamObj.startingYear = team.getInt("rookie_year");
RequestTask rq = new RequestTask();
rq.onResult(s -> {
try {
JSONArray jsonArray = new JSONArray(s);
JSONObject jsonObject = jsonArray.getJSONObject(0);
String base64 = jsonObject.getJSONObject("details").getString("base64Image");
byte[] decodedData = Base64.decode(base64, Base64.DEFAULT);
Bitmap bitmap = BitmapFactory.decodeByteArray(decodedData, 0, decodedData.length);
// System.out.println(base64);
teamObj.bitmap = bitmap;
teamObj.teamColor = frcTeam.findPrimaryColor(bitmap);
Log.i("TBA", "Got icon for team " + teamObj.teamNumber);
} catch (Exception e){
Log.i("TBA", "Failed to icon for team " + teamObj.teamNumber);
} finally {
teams.add(teamObj);
}
return null;
});
rq.execute((TBAAddress + "team/frc" + teamObj.teamNumber + "/media/" + year), TBAHeader);
// ImageRequestTask imageRequestTask = new ImageRequestTask();
//
// imageRequestTask.onResult(bitmap -> {
// teamObj.bitmap = bitmap;
// teamObj.teamColor = frcTeam.findPrimaryColor(bitmap);
// teams.add(teamObj);
//
// return null;
// });
// imageRequestTask.execute("https://www.thebluealliance.com/avatar/" + year + "/frc" + teamObj.teamNumber + ".png");
}
while (teams.size() != teamData.length()) {
Thread.sleep(100);
}
frcEvent event = new frcEvent();
event.name = matchName;
event.eventCode = matchKey;
event.teams = teams;
event.matches = matchData;
FileEditor.setEvent(event);
AlertManager.toast("Saved!");
getActivity().runOnUiThread(() -> findNavController(this).navigate(R.id.action_navigation_tba_event_to_navigation_transfer));
AlertManager.stopLoading();
}catch(Exception j) {
AlertManager.error(j);
AlertManager.stopLoading();
}
});
t.start();
return false;
}
}
@@ -1,504 +0,0 @@
package com.ridgebotics.ridgescout.ui.transfer;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.databinding.FragmentTransferTbaBinding;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.RequestTask;
import com.ridgebotics.ridgescout.types.frcEvent;
import com.ridgebotics.ridgescout.types.frcMatch;
import com.ridgebotics.ridgescout.types.frcTeam;
import com.ridgebotics.ridgescout.utility.fileEditor;
import com.ridgebotics.ridgescout.utility.JSONUtil;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
public class TBAFragment extends Fragment {
private final String TBAAddress = "https://www.thebluealliance.com/api/v3/";
private final String TBAHeader = "X-TBA-Auth-Key: tjEKSZojAU2pgbs2mBt06SKyOakVhLutj3NwuxLTxPKQPLih11aCIwRIVFXKzY4e";
private android.widget.TableLayout Table;
private FragmentTransferTbaBinding binding;
private static final int year = 2024;
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentTransferTbaBinding.inflate(inflater, container, false);
Table = binding.matchTable;
Table.setStretchAllColumns(true);
TableRow tr = new TableRow(getContext());
addTableText(tr, "Loading Events...");
Table.addView(tr);
final RequestTask rq = new RequestTask();
rq.onResult(s -> {
eventTable(s);
return null;
});
rq.execute(TBAAddress + "events/"+year, TBAHeader);
return binding.getRoot();
}
private void addTableText(TableRow tr, String textStr){
TextView text = new TextView(getContext());
text.setTextSize(18);
text.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); // Text align center
text.setText(textStr);
tr.addView(text);
}
public void eventTable(String dataString){
Table.removeAllViews();
Table.setStretchAllColumns(true);
Table.bringToFront();
Date currentTime = Calendar.getInstance().getTime();
try {
JSONArray data = new JSONArray(dataString);
Table.setStretchAllColumns(true);
Table.bringToFront();
boolean toggle = false;
for(int i=0;i<data.length();i++){
TableRow tr = new TableRow(getContext());
TableLayout.LayoutParams rowParams = new TableLayout.LayoutParams(
TableRow.LayoutParams.WRAP_CONTENT,
TableRow.LayoutParams.WRAP_CONTENT
);
rowParams.setMargins(20,20,20,20);
tr.setLayoutParams(rowParams);
tr.setPadding(20,20,20,20);
tr.setBackgroundColor(0x30000000);
JSONObject j = data.getJSONObject(i);
String matchKey = j.getString("key");
String name = j.getString("short_name");
// Sometimes, a short name is not present on TBA Events
if(name.isEmpty()){
name = j.getString("name");
}
TextView tv = new TextView(getContext());
tv.setGravity(Gravity.CENTER_VERTICAL);
tv.setTextSize(12);
tv.setText(j.getString("key"));
tr.addView(tv);
tv = new TextView(getContext());
tv.setTextSize(18);
tv.setText(name);
tr.addView(tv);
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
try {
Date startDate = format.parse(j.getString("start_date"));
Date endDate = format.parse(j.getString("end_date"));
if(currentTime.after(endDate)){
tr.setBackgroundColor(0x30FF0000);
}else if(currentTime.before(startDate)){
tr.setBackgroundColor(0x3000FF00);
}else if(currentTime.after(startDate) && currentTime.before(endDate)){
tr.setBackgroundColor(0x30FFFF00);
}
} catch (Exception e) {
AlertManager.error(e);
}
tr.setOnClickListener(v -> {
Table.removeAllViews();
Table.setStretchAllColumns(true);
Table.bringToFront();
TableRow tr1 = new TableRow(getContext());
addTableText(tr1, "Downloading Teams...");
Table.addView(tr1);
final RequestTask rq = new RequestTask();
rq.onResult(teamsStr -> {
TableRow tr11 = new TableRow(getContext());
addTableText(tr11, "Downloading Matches...");
Table.addView(tr11);
final RequestTask rq1 = new RequestTask();
rq1.onResult(matchesStr -> {
matchTable(matchesStr, teamsStr, j);
return null;
});
rq1.execute((TBAAddress + "event/" + matchKey + "/matches"), TBAHeader);
return null;
});
rq.execute((TBAAddress + "event/" + matchKey + "/teams"), TBAHeader);
});
// tr.addView(cl);
Table.addView(tr, rowParams);
toggle = !toggle;
}
}catch (JSONException j){
AlertManager.alert("Error", "Invalid JSON");
}
}
static class matchComparator implements Comparator<JSONObject>
{
public int compare(JSONObject a, JSONObject b)
{
try {
return a.getInt("match_number") - b.getInt("match_number");
}catch (JSONException j){
return 0;
}
}
}
public void matchTable(String matchesString, String teamsString, JSONObject eventData){
Table.removeAllViews();
Table.setStretchAllColumns(true);
Table.bringToFront();
try {
final JSONArray matchData = new JSONArray(matchesString);
// final JSONArray matchData = new JSONArray();
final JSONArray teamData = new JSONArray(teamsString);
String matchKey = eventData.getString("key");
String matchName = eventData.getString("short_name");
// Sometimes, a short name is not present on TBA Events
if(matchName.isEmpty()){
matchName = eventData.getString("name");
}
// Event code at top
TextView tv = new TextView(getContext());
tv.setLayoutParams(new TableRow.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setText(matchKey);
tv.setTextSize(18);
Table.addView(tv);
// Event Name
tv = new TextView(getContext());
tv.setLayoutParams(new TableRow.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText(matchName);
tv.setTextSize(28);
Table.addView(tv);
// Save button
Button btn = new Button(getContext());
btn.setText("Save");
btn.setTextSize(18);
btn.setLayoutParams(new TableRow.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
Table.addView(btn);
if(teamData.length() == 0){
tv = new TextView(getContext());
tv.setLayoutParams(new TableRow.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText("This event has no teams released yet...");
tv.setTextSize(18);
Table.addView(tv);
tv = new TextView(getContext());
tv.setLayoutParams(new TableRow.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText("This event has no teams released yet...");
tv.setTextSize(18);
Table.addView(tv);
btn.setVisibility(View.GONE);
return;
}else if(matchData.length() == 0){
tv = new TextView(getContext());
tv.setLayoutParams(new TableRow.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText("This event has no matches released yet...");
tv.setTextSize(18);
Table.addView(tv);
tv = new TextView(getContext());
tv.setLayoutParams(new TableRow.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText("Try manually adding practice matches.");
tv.setTextSize(18);
Table.addView(tv);
}
tv = new TextView(getContext());
tv.setLayoutParams(new TableRow.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText("Teams");
tv.setTextSize(28);
Table.addView(tv);
int[] teams = new int[teamData.length()];
for(int i = 0 ; i < teamData.length(); i++){
teams[i] = teamData.getJSONObject(i).getInt("team_number");
}
Arrays.sort(teams);
TableRow tr = null;
for(int i=0; i < teamData.length(); i++){
// frcTeam team = event.teams.get(i);
int num = teams[i];
if(i % 7 == 0){
if(i != 0)
Table.addView(tr);
tr = new TableRow(getContext());
}
TextView text = new TextView(getContext());
text.setTextSize(18);
text.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
text.setText(String.valueOf(num));
// if(fileEditor.fileExist(event.eventCode + "-" + num + ".pitscoutdata")){
// text.setBackgroundColor(0x3000FF00);
// }else{
// text.setBackgroundColor(0x30FF0000);
// }
tr.addView(text);
}
if(tr != null)
Table.addView(tr);
final ArrayList<frcMatch> matchesOBJ = new ArrayList<>();
btn.setOnClickListener(v -> {
if(saveData(matchesOBJ, teamData, eventData)){
AlertManager.alert("Info", "Saved!");
}else{
AlertManager.alert("Error", "Error saving files.");
}
});
tv = new TextView(getContext());
tv.setLayoutParams(new TableRow.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
));
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setText("Matches");
tv.setTextSize(28);
Table.addView(tv);
tr = new TableRow(getContext());
addTableText(tr, "#");
addTableText(tr, "Red-1");
addTableText(tr, "Red-2");
addTableText(tr, "Red-3");
addTableText(tr, "Blue-1");
addTableText(tr, "Blue-2");
addTableText(tr, "Blue-3");
Table.addView(tr);
if(matchData.length() == 0)
return;
final JSONArray sortedMatchData = JSONUtil.sort(matchData, (a, b) -> {
JSONObject ja = (JSONObject)a;
JSONObject jb = (JSONObject)b;
try {
return ja.getInt("match_number") - jb.getInt("match_number");
}catch (JSONException j){
return 0;
}
});
boolean toggle = false;
int matchCount = 1;
for(int a=0;a<sortedMatchData.length();a++){
final JSONObject match = sortedMatchData.getJSONObject(a);
if(!match.getString("comp_level").equals("qm")){
continue;
}
final JSONObject alliances = match.getJSONObject("alliances");
final JSONArray redAlliance = alliances.getJSONObject("red").getJSONArray("team_keys");
final JSONArray blueAlliance = alliances.getJSONObject("blue").getJSONArray("team_keys");
tr = new TableRow(getContext());
if (toggle) {
tr.setBackgroundColor(0x30000000);
}
addTableText(tr, String.valueOf(matchCount));
// addTableText(tr, match.getString("key"));
int[] blueKeys = new int[3];
int[] redKeys = new int[3];
for(int b=0;b<6;b++){
TextView text = new TextView(getContext());
text.setTextSize(18);
text.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); // Text align center
tr.addView(text);
if(b < 3){
String str = redAlliance.getString(b).substring(3);
redKeys[b] = Integer.parseInt(str);
text.setText(str);
text.setBackgroundColor(0x50ff0000);
}else{
String str = blueAlliance.getString(b-3).substring(3);
blueKeys[b-3] = Integer.parseInt(str);
text.setText(str);
text.setBackgroundColor(0x500000ff);
}
}
Table.addView(tr);
frcMatch matchOBJ = new frcMatch();
matchOBJ.matchIndex = matchCount;
matchOBJ.blueAlliance = blueKeys;
matchOBJ.redAlliance = redKeys;
matchesOBJ.add(matchOBJ);
matchCount += 1;
toggle = !toggle;
}
// btn.setOnClickListener(v -> {
// if(saveData(matchesOBJ, teamData, eventData)){
// alert("Info", "Saved!");
// }else{
// alert("Error", "Error saving files.");
// }
// });
}catch (JSONException j){
AlertManager.error(j);
AlertManager.alert("Error", "Invalid JSON");
}
}
private boolean saveData(ArrayList<frcMatch> matchData, JSONArray teamData, JSONObject eventData){
try {
final String matchKey = eventData.getString("key");
String matchName = eventData.getString("short_name");
// Sometimes, a short name is not present on TBA Events
if(matchName.isEmpty()){
matchName = eventData.getString("name");
}
ArrayList<frcTeam> teams = new ArrayList<>();
for(int i=0;i<teamData.length();i++){
frcTeam teamObj = new frcTeam();
JSONObject team = teamData.getJSONObject(i);
teamObj.teamNumber = team.getInt("team_number");
teamObj.teamName = team.getString("nickname");
teamObj.city = team.getString("city");
teamObj.stateOrProv = team.getString("state_prov");
teamObj.school = team.getString("school_name");
teamObj.country = team.getString("country");
teamObj.startingYear = team.getInt("rookie_year");
teams.add(teamObj);
}
frcEvent event = new frcEvent();
event.name = matchName;
event.eventCode = matchKey;
event.teams = teams;
event.matches = matchData;
return fileEditor.setEvent(event);
}catch (JSONException j){
AlertManager.error(j);
AlertManager.alert("Error", "Invalid JSON");
return false;
}
}
}
@@ -0,0 +1,214 @@
package com.ridgebotics.ridgescout.ui.transfer;
import static androidx.navigation.fragment.FragmentKt.findNavController;
import static com.ridgebotics.ridgescout.utility.Colors.tba_current;
import static com.ridgebotics.ridgescout.utility.Colors.tba_next;
import static com.ridgebotics.ridgescout.utility.Colors.tba_previous;
import static com.ridgebotics.ridgescout.utility.FileEditor.TBAAddress;
import static com.ridgebotics.ridgescout.utility.FileEditor.TBAHeader;
import android.app.ProgressDialog;
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.R;
import com.ridgebotics.ridgescout.databinding.FragmentTransferTbaBinding;
import com.ridgebotics.ridgescout.ui.views.TBAEventOption;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.RequestTask;
import com.ridgebotics.ridgescout.utility.SettingsManager;
import com.ridgebotics.ridgescout.utility.builders.TextViewBuilder;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
// Class to select an event from TBA and redirect to TBAEventFragment.java.
public class TBASelectorFragment extends Fragment {
private android.widget.TableLayout Table;
private FragmentTransferTbaBinding binding;
private final int year = SettingsManager.getYearNum();
private ProgressDialog loadingDialog;
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentTransferTbaBinding.inflate(inflater, container, false);
Table = binding.matchTable;
startLoading("Loading Events...");
final RequestTask rq = new RequestTask();
rq.onResult(s -> {
if(s == null || s.isEmpty()) {
AlertManager.error("Could not fetch event!");
stopLoading();
return null;
}
eventTable(s);
return null;
});
rq.execute(TBAAddress + "events/"+year, TBAHeader);
return binding.getRoot();
}
public static int getEventTypeWeight(String type){
switch(type){
case "Preseason": return -3;
case "District": return -2;
case "Regional": return -1;
case "District Championship Division": return 0;
case "District Championship": return 1;
case "Championship Divison": return 2;
case "Championship Finals": return 3;
case "Offseason": return 4;
}
return 0;
}
public void eventTable(String dataString){
Table.removeAllViews();
// Table.setStretchAllColumns(true);
Table.bringToFront();
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
Date currentTime = Calendar.getInstance().getTime();
try {
JSONArray events = new JSONArray(dataString);
List<JSONObject> data = new ArrayList<>();
for(int i = 0; i < events.length(); i++){
data.add(events.getJSONObject(i));
}
// Sort events by date, and then type
data.sort((a, b) -> {
try {
// return (int) (currentTime.getTime() - format.parse(a.getString("start_date")).getTime())
// -
// (int) (currentTime.getTime() - format.parse(b.getString("start_date")).getTime());
int diff = format.parse(a.getString("start_date")).compareTo(format.parse(b.getString("start_date"))) * 10;
if(diff == 0){
diff = getEventTypeWeight(a.getString("event_type_string")) - getEventTypeWeight(b.getString("event_type_string"));
}
if(diff == 0){
diff = a.getString("key").compareTo(b.getString("key"));
}
return diff;
} catch (ParseException | JSONException e) {
AlertManager.error(e);
return 0;
}
});
boolean toggle = false;
for(int i=0;i<data.size();i++){
JSONObject j = data.get(i);
TBAEventOption row = new TBAEventOption(getContext());
// TableRow tr = new TableRow(getContext());
// TableLayout.LayoutParams rowParams = new TableLayout.LayoutParams(
// TableRow.LayoutParams.WRAP_CONTENT,
// TableRow.LayoutParams.WRAP_CONTENT
// );
// rowParams.setMargins(20,20,20,20);
// tr.setLayoutParams(rowParams);
// tr.setPadding(20,20,20,20);
row.setBackgroundColor(0x30000000);
String name = j.getString("short_name");
// Sometimes, a short name is not present on TBA Events
if(name.isEmpty()){
name = j.getString("name");
}
String eventType = j.getString("event_type_string");
if(eventType.equals("District") || eventType.equals("Regional"))
eventType = "Week " + (j.getInt("week")+1) + " " + eventType;
row.setName(name);
row.setCode(j.getString("key"));
row.setType(eventType);
try {
Date startDate = format.parse(j.getString("start_date"));
Date endDate = format.parse(j.getString("end_date"));
if(currentTime.after(endDate)){
row.setColor(tba_previous);
}else if(currentTime.before(startDate)){
row.setColor(tba_next);
}else if(currentTime.after(startDate) && currentTime.before(endDate)){
row.setColor(tba_current);
}
} catch (Exception e) {
AlertManager.error("Failed finding start and end dates!", e);
stopLoading();
}
row.setOnClickListener(v -> {
TBAEventFragment.setEventData(j);
findNavController(this).navigate(R.id.action_navigation_tba_selector_to_navigation_tba_event);
});
// tr.addView(cl);
Table.addView(row);
toggle = !toggle;
}
stopLoading();
}catch (JSONException j){
AlertManager.error("Failed Downloading", j);
stopLoading();
}
}
private void startLoading(String title){
getActivity().runOnUiThread(() -> {
if(loadingDialog != null && loadingDialog.isShowing())
loadingDialog.dismiss();
loadingDialog = ProgressDialog.show(getActivity(), title, "Please wait...");
});
}
private void stopLoading(){
getActivity().runOnUiThread(() -> {
if (loadingDialog != null)
loadingDialog.cancel();
loadingDialog = null;
});
}
}
@@ -3,7 +3,6 @@ package com.ridgebotics.ridgescout.ui.transfer;
import static androidx.navigation.fragment.FragmentKt.findNavController; import static androidx.navigation.fragment.FragmentKt.findNavController;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@@ -14,11 +13,13 @@ 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.settingsManager; import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.SettingsManager;
import com.ridgebotics.ridgescout.databinding.FragmentTransferBinding; import com.ridgebotics.ridgescout.databinding.FragmentTransferBinding;
import com.ridgebotics.ridgescout.ui.transfer.bluetooth.BluetoothSenderFragment; import com.ridgebotics.ridgescout.ui.transfer.bluetooth.BluetoothSenderFragment;
import com.ridgebotics.ridgescout.ui.transfer.codes.CodeGeneratorView; import com.ridgebotics.ridgescout.ui.transfer.codes.CodeGeneratorView;
// Class to do transference.
public class TransferFragment extends Fragment { public class TransferFragment extends Fragment {
private FragmentTransferBinding binding; private FragmentTransferBinding binding;
@@ -31,9 +32,6 @@ public class TransferFragment extends Fragment {
String evcode; String evcode;
private static final int background_color = 0x5000ff00;
private static final int unselected_background_color = 0x2000ff00;
// private Bundle b; // private Bundle b;
@Nullable @Nullable
@@ -45,27 +43,45 @@ public class TransferFragment extends Fragment {
binding = FragmentTransferBinding.inflate(inflater, container, false); binding = FragmentTransferBinding.inflate(inflater, container, false);
evcode = settingsManager.getEVCode(); evcode = SettingsManager.getEVCode();
binding.downloadButton.setOnClickListener(v -> { binding.downloadButton.setOnClickListener(v -> {
start_download(); start_download();
}); });
binding.TBAButton.setOnClickListener(v -> { binding.TBAButton.setOnClickListener(v -> {
binding.noEventError.setVisibility(View.GONE); findNavController(this).navigate(R.id.action_navigation_transfer_to_navigation_tba_selector);
AlertDialog.Builder alert = new AlertDialog.Builder(getContext());
alert.setTitle("Warning");
alert.setMessage("This action requires internet.");
alert.setCancelable(true);
alert.setPositiveButton("Ok", (dialog, which) -> {
findNavController(this).navigate(R.id.action_navigation_transfer_to_navigation_tba);
}); });
alert.setNegativeButton("Cancel", null);
alert.create().show(); if(!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);
HttpSync.sync();
}); });
if(HttpSync.getIsRunning())
binding.SyncButton.setEnabled(false);
HttpSync.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(HttpSync.text);
HttpSync.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 +98,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();
} }
@@ -11,6 +11,7 @@ import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.databinding.FragmentTransferSelectorBinding; import com.ridgebotics.ridgescout.databinding.FragmentTransferSelectorBinding;
// The transfer method selector
public class TransferSelectorFragment extends Fragment { public class TransferSelectorFragment extends Fragment {
// Declaring three blank funcs in one line lol // Declaring three blank funcs in one line lol
@@ -30,17 +31,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();
} }
@@ -8,6 +8,7 @@ import android.bluetooth.BluetoothSocket;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Build; import android.os.Build;
import android.util.Log;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
@@ -20,11 +21,10 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
// Helper class for bluetooth reciever.
public class BluetoothReceiver { public class BluetoothReceiver {
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private static final String NAME = "BluetoothReceiverApp"; private static final String NAME = "BluetoothReceiverApp";
private static final int REQUEST_ENABLE_BT = 1;
private static final int REQUEST_PERMISSIONS = 2;
private Context context; private Context context;
private BluetoothAdapter bluetoothAdapter; private BluetoothAdapter bluetoothAdapter;
@@ -140,7 +140,7 @@ public class BluetoothReceiver {
} catch (IOException e) { } catch (IOException e) {
if (e.getMessage() != null && e.getMessage().contains("bt socket closed, read return: -1")) { if (e.getMessage() != null && e.getMessage().contains("bt socket closed, read return: -1")) {
receiveddata.onConnectionStop(); receiveddata.onConnectionStop();
System.out.println("Bluetooth socket closed, treating as end of stream"); Log.i(getClass().toString(), "Bluetooth socket closed, treating as end of stream");
} else { } else {
throw e; throw e;
} }
@@ -1,6 +1,7 @@
package com.ridgebotics.ridgescout.ui.transfer.bluetooth; package com.ridgebotics.ridgescout.ui.transfer.bluetooth;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@@ -12,16 +13,17 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.databinding.FragmentTransferBluetoothReceiverBinding; import com.ridgebotics.ridgescout.databinding.FragmentTransferBluetoothReceiverBinding;
import com.ridgebotics.ridgescout.types.file; import com.ridgebotics.ridgescout.types.ScoutingFile;
import com.ridgebotics.ridgescout.utility.AlertManager; import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.BuiltByteParser; import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.fileEditor; import com.ridgebotics.ridgescout.utility.FileEditor;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.zip.DataFormatException; import java.util.zip.DataFormatException;
// Class to receive bluetooth transmissions from other devices
public class BluetoothReceiverFragment extends Fragment { public class BluetoothReceiverFragment extends Fragment {
private BluetoothReceiver bluetoothReceiver; private BluetoothReceiver bluetoothReceiver;
private Button startListeningButton; private Button startListeningButton;
@@ -68,12 +70,12 @@ public class BluetoothReceiverFragment extends Fragment {
statusTextView = binding.statusTextView; statusTextView = binding.statusTextView;
if (!bluetoothReceiver.isBluetoothSupported()) { if (!bluetoothReceiver.isBluetoothSupported()) {
AlertManager.error("Bluetooth is not supported on this device"); AlertManager.addSimpleError("Bluetooth is not supported on this device");
return binding.getRoot(); return binding.getRoot();
} }
if (!bluetoothReceiver.isBluetoothEnabled()) { if (!bluetoothReceiver.isBluetoothEnabled()) {
AlertManager.error("Please enable Bluetooth"); AlertManager.addSimpleError("Please enable Bluetooth");
} }
startListeningButton.setOnClickListener(v -> { startListeningButton.setOnClickListener(v -> {
@@ -97,7 +99,7 @@ public class BluetoothReceiverFragment extends Fragment {
recievedBytes = new ArrayList<>(); recievedBytes = new ArrayList<>();
} catch (IOException e) { } catch (IOException e) {
AlertManager.error("Failed to start listening: " + e.getMessage()); AlertManager.error("Failed to start listening", e);
} }
} }
@@ -108,15 +110,15 @@ public class BluetoothReceiverFragment extends Fragment {
startListeningButton.setEnabled(true); startListeningButton.setEnabled(true);
stopListeningButton.setEnabled(false); stopListeningButton.setEnabled(false);
} catch (IOException e) { } catch (IOException e) {
AlertManager.error("Failed to stop listening: " + e.getMessage()); AlertManager.error("Failed to stop listening: " + e.getMessage(), e);
} }
} }
private List<byte[]> recievedBytes; private List<byte[]> recievedBytes;
private void receiveData(byte[] data, int bytes) { private void receiveData(byte[] data, int bytes) {
byte[] newBytes = fileEditor.getByteBlock(data, 0, bytes); byte[] newBytes = FileEditor.getByteBlock(data, 0, bytes);
System.out.println("Recieved " + bytes + " Bytes over bluetooth!"); Log.i(getClass().toString(), "Recieved " + bytes + " Bytes over bluetooth!");
recievedBytes.add(newBytes); recievedBytes.add(newBytes);
} }
@@ -125,19 +127,19 @@ public class BluetoothReceiverFragment extends Fragment {
String result_filenames = ""; String result_filenames = "";
try { try {
byte[] resultBytes = fileEditor.combineByteArrays(recievedBytes); byte[] resultBytes = FileEditor.combineByteArrays(recievedBytes);
resultBytes = fileEditor.blockUncompress(resultBytes); resultBytes = FileEditor.blockUncompress(resultBytes);
BuiltByteParser bbp = new BuiltByteParser(resultBytes); BuiltByteParser bbp = new BuiltByteParser(resultBytes);
ArrayList<BuiltByteParser.parsedObject> result = bbp.parse(); ArrayList<BuiltByteParser.parsedObject> result = bbp.parse();
for (int i = 0; i < result.size(); i++) { for (int i = 0; i < result.size(); i++) {
if (result.get(i).getType() != file.typecode) continue; if (result.get(i).getType() != ScoutingFile.typecode) continue;
file f = file.decode((byte[]) result.get(i).get()); ScoutingFile f = ScoutingFile.decode((byte[]) result.get(i).get());
if (f != null) { if (f != null) {
System.out.println(f.filename); Log.i(getClass().toString(), f.filename);
if (f.write()) if (f.write())
result_filenames += f.filename + "\n"; result_filenames += f.filename + "\n";
} }
@@ -21,10 +21,9 @@ import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
// Helper class with sending bluetooth scouting data
public class BluetoothSender { public class BluetoothSender {
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private static final int REQUEST_ENABLE_BT = 1;
private static final int REQUEST_PERMISSIONS = 2;
private Context context; private Context context;
private BluetoothAdapter bluetoothAdapter; private BluetoothAdapter bluetoothAdapter;
@@ -17,12 +17,13 @@ import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.databinding.FragmentTransferBluetoothSenderBinding; import com.ridgebotics.ridgescout.databinding.FragmentTransferBluetoothSenderBinding;
import com.ridgebotics.ridgescout.utility.AlertManager; import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.fileEditor; import com.ridgebotics.ridgescout.utility.FileEditor;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Set; import java.util.Set;
// Class to send scouting data over bluetooth.
public class BluetoothSenderFragment extends Fragment { public class BluetoothSenderFragment extends Fragment {
private BluetoothSender bluetoothSender; private BluetoothSender bluetoothSender;
private ListView deviceListView; private ListView deviceListView;
@@ -101,10 +102,10 @@ public class BluetoothSenderFragment extends Fragment {
private void sendData() { private void sendData() {
try { try {
byte[] compressed = fileEditor.blockCompress(data_to_send); byte[] compressed = FileEditor.blockCompress(data_to_send, FileEditor.lengthHeaderBytes);
for(int i = 0; i < Math.ceil((double) compressed.length/1024); i++){ for(int i = 0; i < Math.ceil((double) compressed.length/1024); i++){
bluetoothSender.sendData(fileEditor.getByteBlock(compressed, i*1024, (i+1)*1024)); bluetoothSender.sendData(FileEditor.getByteBlock(compressed, i*1024, (i+1)*1024));
} }
bluetoothSender.close(); bluetoothSender.close();
@@ -0,0 +1,135 @@
package com.ridgebotics.ridgescout.ui.transfer.codes;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.AsyncTask;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.FileEditor;
import com.ridgebotics.ridgescout.utility.TaskRunner;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.function.Function;
public class CodeGenTask implements Callable<List<Bitmap>> {
// private Function<List<Bitmap>, String> resultFunction = null;
//
// @Override
// protected List<Bitmap> doInBackground(String... strings) {
//
//
// return new ArrayList<>();
// }
//
//
// public void onResult(Function<List<Bitmap>, String> func) {
// this.resultFunction = func;
// }
//
//
// @Override
// protected void onPostExecute(List<Bitmap> result) {
// super.onPostExecute(result);
// if(resultFunction != null){
// resultFunction.apply(result);
// }
// }
private final String data;
private final int randID;
private final int qrSize;
private final int qrCount;
private final int imageSize;
public CodeGenTask(String data, int randID, int qrSize, int qrCount, int imageSize) {
this.data = data;
this.randID = randID;
this.qrSize = qrSize;
this.qrCount = qrCount;
this.imageSize = imageSize;
}
@Override
public List<Bitmap> call() {
List<Bitmap> qrBitmaps = new ArrayList<>();
for(int i=0;i<=((data.length()+1)/qrSize);i++){
final int start = i*qrSize;
int end = (i+1)*qrSize;
if(end >= data.length()){
end = data.length();
}
try {
Bitmap unscaledBitmap = generateQrCode(
FileEditor.byteToChar(FileEditor.internalDataVersion, FileEditor.lengthHeaderBytes) +
String.valueOf(FileEditor.byteToChar(randID, FileEditor.lengthHeaderBytes)) +
FileEditor.byteToChar(i, FileEditor.lengthHeaderBytes) +
FileEditor.byteToChar(qrCount - 1, FileEditor.lengthHeaderBytes) +
data.substring(start, end)
);
if(unscaledBitmap == null) {
AlertManager.error("Generated image was null!");
continue;
}
qrBitmaps.add(Bitmap.createScaledBitmap(unscaledBitmap, imageSize, imageSize, false));
// alert("title", ""+(qrCount-1));
}catch (WriterException e){
AlertManager.error(e);
}
}
return qrBitmaps;
}
private Bitmap generateQrCode(String contents) throws WriterException {
final int size = 512;
if (contents == null) {
return null;
}
Map<EncodeHintType, Object> hints = new EnumMap<>(EncodeHintType.class);
// The Charset must be UTF-8, Or data will not be transferred properly. IDK why.
hints.put(EncodeHintType.CHARACTER_SET, "ISO-8859-1");
// hints.put(EncodeHintType.);
hints.put(EncodeHintType.MARGIN, 0); /* default = 4 */
MultiFormatWriter writer = new MultiFormatWriter();
BitMatrix result;
try {
result = writer.encode(contents, BarcodeFormat.DATA_MATRIX, size, size, hints);
} catch (IllegalArgumentException e) {
// Unsupported format
AlertManager.error(e);
return null;
}
int width = result.getWidth();
int height = result.getHeight();
int[] pixels = new int[width * height];
for (int y = 0; y < height; y++) {
int offset = y * width;
for (int x = 0; x < width; x++) {
pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.WHITE;
}
}
Bitmap bitmap = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
return bitmap;
}
}
@@ -1,9 +1,10 @@
package com.ridgebotics.ridgescout.ui.transfer.codes; package com.ridgebotics.ridgescout.ui.transfer.codes;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.Bundle; import android.os.Bundle;
import android.os.CountDownTimer; import android.os.CountDownTimer;
import android.os.Handler;
import android.util.DisplayMetrics;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@@ -17,19 +18,15 @@ import androidx.fragment.app.Fragment;
import com.ridgebotics.ridgescout.databinding.FragmentTransferCodeSenderBinding; import com.ridgebotics.ridgescout.databinding.FragmentTransferCodeSenderBinding;
import com.ridgebotics.ridgescout.utility.AlertManager; import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.fileEditor; import com.ridgebotics.ridgescout.utility.FileEditor;
import com.google.zxing.BarcodeFormat; import com.ridgebotics.ridgescout.utility.TaskRunner;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.EnumMap; import java.util.List;
import java.util.Map;
import java.util.Random; import java.util.Random;
// Class to show the code transfer thing.
public class CodeGeneratorView extends Fragment { public class CodeGeneratorView extends Fragment {
private ImageView qrImage; private ImageView qrImage;
private SeekBar qrSpeedSlider; private SeekBar qrSpeedSlider;
@@ -37,23 +34,28 @@ public class CodeGeneratorView extends Fragment {
private TextView qrIndexN; private TextView qrIndexN;
private TextView qrIndexD; private TextView qrIndexD;
private final int maxQrCount = 256; //The max number that can be stored in a byte private static final int maxQrCount = 256; //The max number that can be stored in a byte
private final int maxQrSpeed = 5; private static final int maxQrSize = 800;
private final int minQrSpeed = 300 + maxQrSpeed - 1;
private static final int maxQrSpeed = 50;
private static final int minQrSpeed = 1000;
private static final int defaultQrDelay = 12;
private int imageSize;
private int minQrSize = 0; private int minQrSize = 0;
private final int maxQrSize = 800;
private int qrSize = 200; private int qrSize = 200;
private final int defaultQrDelay = 419; private double qrDelay = 0;
private int qrDelay = 0;
private int qrIndex = 0; private int qrIndex = 0;
private CountDownTimer timer; private CountDownTimer timer;
private int qrCount = 0; private int qrCount = 0;
private ArrayList<Bitmap> qrBitmaps; private List<Bitmap> qrBitmaps = new ArrayList<>();
private FragmentTransferCodeSenderBinding binding; private FragmentTransferCodeSenderBinding binding;
@@ -76,7 +78,13 @@ public class CodeGeneratorView extends Fragment {
qrIndexN = binding.qrIndexN; qrIndexN = binding.qrIndexN;
qrIndexD = binding.qrIndexD; qrIndexD = binding.qrIndexD;
String compressed = new String(fileEditor.blockCompress(data), StandardCharsets.ISO_8859_1); DisplayMetrics displaymetrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
// int height = displaymetrics.heightPixels;
imageSize = displaymetrics.widthPixels;
// = 800;
String compressed = new String(FileEditor.blockCompress(data, FileEditor.lengthHeaderBytes), StandardCharsets.ISO_8859_1);
if(compressed.isEmpty()){ if(compressed.isEmpty()){
AlertManager.alert("Error!", "Empty data!"); AlertManager.alert("Error!", "Empty data!");
@@ -84,75 +92,30 @@ public class CodeGeneratorView extends Fragment {
} }
minQrSize = Math.round((float)compressed.length() / maxQrCount)+1; minQrSize = Math.round((float)compressed.length() / maxQrCount)+1;
qrSize += minQrSize;
qrSizeSlider.setMax(maxQrSize-minQrSize);
qrSpeedSlider.setMax((minQrSpeed-maxQrSpeed)*2);
qrSizeSlider.setProgress(minQrSize+qrSize);
qrSpeedSlider.setProgress(defaultQrDelay+5);
sendData(compressed); sendData(compressed);
qrSpeedSlider.setMax(maxQrSpeed*2);
qrSpeedSlider.setProgress(maxQrSpeed + defaultQrDelay);
qrSizeSlider.setMax(maxQrSize-minQrSize);
qrSizeSlider.setProgress(qrSize-minQrSize);
startLoop();
return binding.getRoot(); return binding.getRoot();
} }
private Bitmap generateQrCode(String contents) throws WriterException {
final int size = 512;
if (contents == null) {
return null;
}
Map<EncodeHintType, Object> hints = new EnumMap<>(EncodeHintType.class);
// The Charset must be UTF-8, Or data will not be transferred properly. IDK why.
hints.put(EncodeHintType.CHARACTER_SET, "ISO-8859-1");
// hints.put(EncodeHintType.);
hints.put(EncodeHintType.MARGIN, 0); /* default = 4 */
MultiFormatWriter writer = new MultiFormatWriter();
BitMatrix result;
try {
result = writer.encode(contents, BarcodeFormat.DATA_MATRIX, size, size, hints);
} catch (IllegalArgumentException e) {
// Unsupported format
AlertManager.error(e);
return null;
}
int width = result.getWidth();
int height = result.getHeight();
int[] pixels = new int[width * height];
for (int y = 0; y < height; y++) {
int offset = y * width;
for (int x = 0; x < width; x++) {
pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.WHITE;
}
}
Bitmap bitmap = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
return bitmap;
}
private void sendData(String data){ private void sendData(String data){
qrCount = (data.length()/qrSize)+1; qrCount = (data.length()/qrSize)+1;
qrIndexD.setText(String.valueOf(qrCount)); qrIndexD.setText(String.valueOf(qrCount));
// alert("size", ""+binding.qrSizeSlider.getProgress()+"\n"+binding.qrSizeSlider.getMax());
qrSpeedSlider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { qrSpeedSlider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override @Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
qrDelay = -(minQrSpeed - progress - maxQrSpeed + 1); qrDelay = ((double) progress /maxQrSpeed) - 1;
// startLoop();
} }
@Override @Override
public void onStartTrackingTouch(SeekBar seekBar) {} public void onStartTrackingTouch(SeekBar seekBar) {}
@@ -172,43 +135,24 @@ public class CodeGeneratorView extends Fragment {
qrCount = ((data.length()+1)/qrSize) +1; qrCount = ((data.length()+1)/qrSize) +1;
qrIndexD.setText(String.valueOf(qrCount)); qrIndexD.setText(String.valueOf(qrCount));
sendData(data); sendData(data);
// startLoop();
} }
}); });
// qrSizeSlider.setProgress(qr); AlertManager.startLoading("Generating codes...");
qrBitmaps = new ArrayList<>(); new TaskRunner().executeAsync(new CodeGenTask(data, new Random().nextInt(255), qrSize, qrCount, imageSize), result -> {
qrBitmaps = result;
int randID = new Random().nextInt(255); AlertManager.stopLoading();
for(int i=0;i<=((data.length()+1)/qrSize);i++){
final int start = i*qrSize;
int end = (i+1)*qrSize;
if(end >= data.length()){
end = data.length();
}
try {
// alert("test", ""+Math.ceil((double)data.length()/(double)qrSize));
qrBitmaps.add(generateQrCode(
fileEditor.byteToChar(fileEditor.internalDataVersion) +
String.valueOf(fileEditor.byteToChar(randID)) +
fileEditor.byteToChar(i) +
fileEditor.byteToChar(qrCount - 1) +
data.substring(start, end)
));
// alert("title", ""+(qrCount-1));
}catch (WriterException e){
AlertManager.error(e);
}
}
qrIndex = 0; qrIndex = 0;
if(timer != null){ });
timer.cancel();
}
qrLoop();
} }
private void updateQr(){ private void updateQr(){
if(qrBitmaps.isEmpty())
return;
qrImage.setImageBitmap(qrBitmaps.get(qrIndex)); qrImage.setImageBitmap(qrBitmaps.get(qrIndex));
if(qrDelay > 0) { if(qrDelay > 0) {
this.qrIndex += 1; this.qrIndex += 1;
@@ -225,13 +169,36 @@ public class CodeGeneratorView extends Fragment {
qrIndexN.setText(String.valueOf(qrIndex+1)); qrIndexN.setText(String.valueOf(qrIndex+1));
} }
private void qrLoop(){ private boolean shouldstop = false;
timer = new CountDownTimer(minQrSpeed-Math.abs(qrDelay)+1, 1000) {
public void onTick(long millisUntilFinished) {} private void startLoop() {
public void onFinish() { final Handler handler = new Handler();
Runnable runnable = new Runnable() {
@Override
public void run() {
if(shouldstop){
return;
}
try{
updateQr(); updateQr();
qrLoop();
} }
}.start(); catch (Exception e) {
AlertManager.error(e);
}
finally{
double a = ((double) maxQrSpeed) / (Math.abs(qrDelay));
a = Math.min(Math.max(a, maxQrSpeed), minQrSpeed);
handler.postDelayed(this, (long) a);
}
}
};
handler.post(runnable);
}
@Override
public void onDestroy() {
super.onDestroy();
shouldstop = true;
} }
} }
@@ -2,6 +2,10 @@ package com.ridgebotics.ridgescout.ui.transfer.codes;
// From https://github.com/dlazaro66/QRCodeReaderView/blob/master/samples/src/main/java/com/example/qr_readerexample/PointsOverlayView.java // From https://github.com/dlazaro66/QRCodeReaderView/blob/master/samples/src/main/java/com/example/qr_readerexample/PointsOverlayView.java
import static com.ridgebotics.ridgescout.utility.Colors.code_scanned_color;
import static com.ridgebotics.ridgescout.utility.Colors.code_selected_color;
import static com.ridgebotics.ridgescout.utility.Colors.code_unscanned_color;
import android.content.Context; import android.content.Context;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Color;
@@ -13,6 +17,7 @@ import android.view.View;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
// Shows the progress of receiving data over codes.
public class CodeOverlayView extends View { public class CodeOverlayView extends View {
PointF[] points; PointF[] points;
@@ -37,7 +42,7 @@ public class CodeOverlayView extends View {
private void init() { private void init() {
paint = new Paint(); paint = new Paint();
paint.setColor(Color.YELLOW); paint.setColor(code_selected_color);
paint.setStyle(Paint.Style.FILL); paint.setStyle(Paint.Style.FILL);
} }
@@ -60,7 +65,7 @@ public class CodeOverlayView extends View {
} }
} }
if(barColors != null){ if(barColors != null){
final double width = getWidth()/barColors.length; final double width = (double) getWidth() /barColors.length;
final int top = 0; final int top = 0;
final int bottom = barHeight; final int bottom = barHeight;
@@ -69,12 +74,12 @@ public class CodeOverlayView extends View {
final int num = barColors[i]; final int num = barColors[i];
int c = Color.RED; int c = code_unscanned_color;
if(num == 2){ if(num == 2){
c = Color.GREEN; c = code_scanned_color;
}else if(num == 1){ }else if(num == 1){
c = Color.YELLOW; c = code_selected_color;
} }
final Paint p = new Paint(); final Paint p = new Paint();
@@ -14,18 +14,69 @@ import com.google.zxing.Reader;
import com.google.zxing.Result; import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer; import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.datamatrix.DataMatrixReader; import com.google.zxing.datamatrix.DataMatrixReader;
import com.ridgebotics.ridgescout.utility.TaskRunner;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Callable;
import java.util.function.Function; import java.util.function.Function;
public class CodeScanTask extends AsyncTask<String, String, String>{ // Background task for code scanning, to not slow down the scanner.
private Function<String, String> resultFunction = null; //public class CodeScanTask extends AsyncTask<String, String, String>{
// private Function<String, String> resultFunction = null;
// private Bitmap image;
//
// @Override
// protected String doInBackground(String... str) {
// if(image == null){return null;}
//
// int width = image.getWidth();
// int height = image.getHeight();
// int[] pixels = new int[width * height];
// image.getPixels(pixels, 0, width, 0, 0, width, height);
//
// RGBLuminanceSource source = new RGBLuminanceSource(width, height, pixels);
// BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));
//
// Map<DecodeHintType, Object> hints = new HashMap<>();
// hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
//// hints.put(DecodeHintType.PURE_BARCODE, Boolean.TRUE);
// hints.put(DecodeHintType.POSSIBLE_FORMATS, EnumSet.of(BarcodeFormat.DATA_MATRIX));
//
// Reader reader = new DataMatrixReader();
// try {
// Result result = reader.decode(binaryBitmap, hints);
// return result.getText();
// } catch (NotFoundException | ChecksumException | FormatException e) {
//// AlertManager.error(e);
// }
//
// return null;
// }
// public void setImage(Bitmap image){this.image = image;}
// public void onResult(Function<String, String> func) {
// this.resultFunction = func;
// }
//
// @Override
// protected void onPostExecute(String result) {
// super.onPostExecute(result);
// if(resultFunction != null){
// resultFunction.apply(result);
// }
// }
//}
public class CodeScanTask implements Callable<String> {
private Bitmap image; private Bitmap image;
public CodeScanTask(Bitmap image) {
this.image = image;
}
@Override @Override
protected String doInBackground(String... str) { public String call() {
if(image == null){return null;} if(image == null){return null;}
int width = image.getWidth(); int width = image.getWidth();
@@ -51,17 +102,4 @@ public class CodeScanTask extends AsyncTask<String, String, String>{
return null; return null;
} }
public void setImage(Bitmap image){this.image = image;}
public void onResult(Function<String, String> func) {
this.resultFunction = func;
} }
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
if(resultFunction != null){
resultFunction.apply(result);
}
}
}
@@ -2,8 +2,11 @@ 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.graphics.Matrix;
import android.media.Image; import android.media.Image;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
@@ -25,27 +28,31 @@ 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;
import com.ridgebotics.ridgescout.databinding.FragmentTransferCodeReceiverBinding; import com.ridgebotics.ridgescout.databinding.FragmentTransferCodeReceiverBinding;
import com.ridgebotics.ridgescout.databinding.FragmentTransferCodeSenderBinding; import com.ridgebotics.ridgescout.types.ScoutingFile;
import com.ridgebotics.ridgescout.types.file;
import com.ridgebotics.ridgescout.utility.AlertManager; import com.ridgebotics.ridgescout.utility.AlertManager;
import com.ridgebotics.ridgescout.utility.BuiltByteParser; import com.ridgebotics.ridgescout.utility.BuiltByteParser;
import com.ridgebotics.ridgescout.utility.fileEditor; import com.ridgebotics.ridgescout.utility.FileEditor;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.ridgebotics.ridgescout.utility.TaskRunner;
import org.checkerframework.checker.units.qual.C;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
//public class scannerView extends androidx.appcompat.widget.AppCompatImageView { // Recieves data from the camera, scanning codes.
public class CodeScannerView extends Fragment { public class CodeScannerView extends Fragment {
private CodeOverlayView CodeOverlayView; private CodeOverlayView CodeOverlayView;
private Handler uiHandler; private Handler uiHandler;
@@ -60,21 +67,12 @@ public class CodeScannerView extends Fragment {
alert.create().show(); alert.create().show();
} }
private float scale = 0;
private final int downscale = 1; private final int downscale = 1;
private LifecycleOwner lifecycle; private LifecycleOwner lifecycle;
private void setImage(Bitmap bmp){ private void setImage(Bitmap bmp){
if(scale == 0) {
scale = ((float) binding.container.getWidth() / bmp.getWidth()) * ((float) 16 / 9);
binding.scannerImage.setTranslationX(0);
binding.scannerImage.setTranslationY(0);
}
scanQRCode(bmp); scanQRCode(bmp);
binding.scannerImage.setImageBitmap(bmp); binding.scannerImage.setImageBitmap(bmp);
binding.scannerThreshold.bringToFront();
// alert("test", getChildCount()+"");
} }
// private Bitmap img // private Bitmap img
@@ -97,37 +95,38 @@ public class CodeScannerView extends Fragment {
final int height = image.getHeight(); final int height = image.getHeight();
int[] pixels = new int[width * height]; int[] pixels = new int[width * height];
for (int y = 0; y < height; y++) { for (int i = 0; i < width*height; i++) {
for (int x = 0; x < width; x++) { int L = levelMap[yBuffer.get(i) & 0xff];
int L = levelMap[yBuffer.get() & 0xff]; pixels[i] = 0xff000000 | (L << 16) | (L << 8) | L;
pixels[y * width + x] = 0xff000000 | (L << 16) | (L << 8) | L;
}
} }
Matrix matrix = new Matrix();
matrix.postRotate(90);
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height); bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
// Bitmap.rota
return bitmap; return bitmap;
} }
public void scanQRCode(Bitmap bitmap) { public void scanQRCode(Bitmap bitmap) {
CodeScanTask async = new CodeScanTask(); // CodeScanTask async = new CodeScanTask();
async.setImage(bitmap); new TaskRunner().executeAsync(new CodeScanTask(bitmap), data -> {
async.onResult(data -> {
if(data != null){ if(data != null){
// alert("test", ""+fileEditor.byteFromChar(data.charAt(0))); // alert("test", ""+fileEditor.byteFromChar(data.charAt(0)));
compileData( compileData(
fileEditor.byteFromChar(data.charAt(0)), FileEditor.byteFromChar(data.charAt(0)),
fileEditor.byteFromChar(data.charAt(1)), FileEditor.byteFromChar(data.charAt(1)),
fileEditor.byteFromChar(data.charAt(2)), FileEditor.byteFromChar(data.charAt(2)),
(fileEditor.byteFromChar(data.charAt(3))+1), (FileEditor.byteFromChar(data.charAt(3))+1),
data.substring(4) data.substring(4)
); );
} }
return null;
}); });
async.execute();
// return contents; // return contents;
@@ -150,6 +149,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();
@@ -226,26 +228,23 @@ public class CodeScannerView extends Fragment {
void bindPreview(@NonNull ProcessCameraProvider cameraProvider) { void bindPreview(@NonNull ProcessCameraProvider cameraProvider) {
Preview preview = new Preview.Builder() Preview preview = new Preview.Builder().build();
.setTargetAspectRatio(AspectRatio.RATIO_16_9)
.setTargetRotation(Surface.ROTATION_180)
.build();
CameraSelector cameraSelector = new CameraSelector.Builder() CameraSelector cameraSelector = new CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK) .requireLensFacing(CameraSelector.LENS_FACING_BACK)
// .addCameraFilter(CameraFilters.NON) // .addCameraFilter(CameraFilters.NON)
.build(); .build();
ExecutorService executor = Executors.newSingleThreadExecutor(); ExecutorService executor = Executors.newSingleThreadExecutor();
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder() ImageAnalysis imageAnalysis = new ImageAnalysis.Builder()
// .setTargetResolution(new Size(224, 224)) // .setTargetAspectRatio(AspectRatio.RATIO_16_9)
.setOutputImageRotationEnabled(false)
.setTargetAspectRatio(AspectRatio.RATIO_16_9)
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
// .setOutputImageRotationEnabled(true)
// .setTargetRotation(Surface.ROTATION_0)
.build(); .build();
imageAnalysis.setAnalyzer(executor, new ImageAnalysis.Analyzer() { imageAnalysis.setAnalyzer(executor, new ImageAnalysis.Analyzer() {
@OptIn(markerClass = ExperimentalGetImage.class) @Override @OptIn(markerClass = ExperimentalGetImage.class) @Override
public void analyze(@NonNull ImageProxy image) { public void analyze(@NonNull ImageProxy image) {
@@ -264,6 +263,7 @@ public class CodeScannerView extends Fragment {
}); });
cameraProvider.unbindAll(); cameraProvider.unbindAll();
// cameraProvider.ro
cameraProvider.bindToLifecycle(lifecycle, cameraProvider.bindToLifecycle(lifecycle,
cameraSelector, imageAnalysis, preview); cameraSelector, imageAnalysis, preview);
@@ -278,8 +278,8 @@ public class CodeScannerView extends Fragment {
private int randID; private int randID;
private int prevQrIndex; private int prevQrIndex;
private void compileData(int dataVersion, int randID, int qrIndex, int qrCount, String qrData){ private void compileData(int dataVersion, int randID, int qrIndex, int qrCount, String qrData){
if(dataVersion != fileEditor.internalDataVersion){ if(dataVersion != FileEditor.internalDataVersion){
alert("Error", "Incorrect data version ("+dataVersion+" != "+fileEditor.internalDataVersion+")"); alert("Error", "Incorrect data version ("+dataVersion+" != "+ FileEditor.internalDataVersion+")");
return; return;
} }
@@ -290,6 +290,7 @@ public class CodeScannerView extends Fragment {
Log.i("title", ""+qrCount); Log.i("title", ""+qrCount);
barColors = new int[qrCount]; barColors = new int[qrCount];
prevQrIndex = qrIndex; prevQrIndex = qrIndex;
qrScannedCount = 0;
} }
final boolean updated; final boolean updated;
@@ -308,14 +309,26 @@ public class CodeScannerView extends Fragment {
if(updated && qrScannedCount >= qrCount){ if(updated && qrScannedCount >= qrCount){
AlertManager.startLoading("Decoding data...");
new TaskRunner().executeAsync(new CodeDecodeTask(), result -> {
AlertManager.stopLoading();
});
}
prevQrIndex = qrIndex;
}
private class CodeDecodeTask implements Callable<Void> {
@Override
public Void call() {
String compiledString = ""; String compiledString = "";
for(int i=0;i<qrCount;i++){ for(int i=0;i<qrDataArr.length;i++){
compiledString += qrDataArr[i]; compiledString += qrDataArr[i];
} }
try { try {
byte[] compiledBytes = compiledString.getBytes(StandardCharsets.ISO_8859_1); byte[] compiledBytes = compiledString.getBytes(StandardCharsets.ISO_8859_1);
byte[] resultBytes = fileEditor.blockUncompress(compiledBytes); byte[] resultBytes = FileEditor.blockUncompress(compiledBytes);
String result_filenames = ""; String result_filenames = "";
@@ -324,8 +337,8 @@ public class CodeScannerView extends Fragment {
ArrayList<BuiltByteParser.parsedObject> result = bbp.parse(); ArrayList<BuiltByteParser.parsedObject> result = bbp.parse();
for(int i = 0; i < result.size(); i++){ for(int i = 0; i < result.size(); i++){
if(result.get(i).getType() != file.typecode) continue; if(result.get(i).getType() != ScoutingFile.typecode) continue;
file f = file.decode((byte[]) result.get(i).get()); ScoutingFile f = ScoutingFile.decode((byte[]) result.get(i).get());
if(f != null) if(f != null)
if(f.write()) if(f.write())
@@ -337,9 +350,8 @@ public class CodeScannerView extends Fragment {
}catch (Exception e){ }catch (Exception e){
AlertManager.error(e); AlertManager.error(e);
} }
}
prevQrIndex = qrIndex;
}
return null;
}
}
} }
@@ -0,0 +1,204 @@
package com.ridgebotics.ridgescout.ui.views;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
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;
// Unused. AI-generated dynamic wallpaper for 2025 season.
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,108 @@
package com.ridgebotics.ridgescout.ui.views;
import static com.ridgebotics.ridgescout.utility.Colors.candlestick_color;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
// Shows the scale for a candlestick chart.
public class CandlestickHeader extends View {
private float absoluteMin = 0;
private float absoluteMax = 100;
// Number of scale marks to show
private final int SCALE_MARKS = 5;
// Padding and dimensions
private final int PADDING_LEFT = 50;
private final int PADDING_RIGHT = 50;
private final int PADDING_TOP = 10;
private final int PADDING_BOTTOM = 10;
private final int TICK_HEIGHT = 10;
private Paint linePaint;
private Paint textPaint;
public CandlestickHeader(Context context) {
super(context);
init();
}
public CandlestickHeader(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CandlestickHeader(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
linePaint.setColor(candlestick_color);
linePaint.setStrokeWidth(2f);
linePaint.setStyle(Paint.Style.STROKE);
textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
textPaint.setColor(candlestick_color);
textPaint.setTextSize(30f);
textPaint.setTextAlign(Paint.Align.CENTER);
}
/**
* Set the scale range for the header
*
* @param absoluteMin Absolute minimum of the dataset
* @param absoluteMax Absolute maximum of the dataset
*/
public void setScale(float absoluteMin, float absoluteMax) {
this.absoluteMin = absoluteMin;
this.absoluteMax = absoluteMax;
invalidate();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int desiredWidth = 600;
int desiredHeight = 80; // Height for the scale with labels
int width = resolveSize(desiredWidth, widthMeasureSpec);
int height = resolveSize(desiredHeight, heightMeasureSpec);
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
// Draw horizontal axis
float y = height - PADDING_BOTTOM;
canvas.drawLine(PADDING_LEFT, y, width - PADDING_RIGHT, y, linePaint);
// Calculate interval between scale marks
float availableWidth = width - PADDING_LEFT - PADDING_RIGHT;
float intervalValue = (absoluteMax - absoluteMin) / (SCALE_MARKS - 1);
float intervalPixels = availableWidth / (SCALE_MARKS - 1);
// Draw scale marks and labels
for (int i = 0; i < SCALE_MARKS; i++) {
float x = PADDING_LEFT + (intervalPixels * i);
float value = absoluteMin + (intervalValue * i);
// Draw tick mark
canvas.drawLine(x, y, x, y - TICK_HEIGHT, linePaint);
// Draw label
canvas.drawText(String.format("%.1f", value), x, y - TICK_HEIGHT - 10, textPaint);
}
}
}
@@ -0,0 +1,215 @@
package com.ridgebotics.ridgescout.ui.views;
import static com.ridgebotics.ridgescout.utility.Colors.candlestick_color;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import com.ridgebotics.ridgescout.types.data.RawDataType;
import com.ridgebotics.ridgescout.ui.data.DataProcessing;
import java.util.List;
// A single candlestick chart row.
public class CandlestickView extends View {
// Data points
private float min = 0;
private float max = 0;
private float lowerQuartile = 0;
private float upperQuartile = 0;
public float average = 0;
// Dataset absolute bounds for scaling
private float absoluteMin = 0;
private float absoluteMax = 100;
// Padding and dimensions
private final int PADDING_LEFT = 50;
private final int PADDING_RIGHT = 50;
private final int PADDING_TOP = 20;
private final int PADDING_BOTTOM = 20;
private final int CANDLESTICK_HEIGHT = 60;
private final int WHISKER_HEIGHT = 10;
// Paint objects
private Paint boxPaint;
private Paint whiskerPaint;
private Paint averagePaint;
private Paint textPaint;
public CandlestickView(Context context) {
super(context);
init();
}
public CandlestickView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CandlestickView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
// Initialize paint objects
boxPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
boxPaint.setColor(candlestick_color);
boxPaint.setStyle(Paint.Style.STROKE);
boxPaint.setStrokeWidth(2f);
whiskerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
whiskerPaint.setColor(candlestick_color);
whiskerPaint.setStrokeWidth(2f);
whiskerPaint.setStyle(Paint.Style.STROKE);
averagePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
averagePaint.setColor(candlestick_color);
averagePaint.setStrokeWidth(3f);
textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
textPaint.setColor(candlestick_color);
textPaint.setTextSize(30f);
}
/**
* Set data for the candlestick chart
*
* @param min Minimum value
* @param lowerQuartile Lower quartile value
* @param average Average value
* @param upperQuartile Upper quartile value
* @param max Maximum value
* @param absoluteMin Absolute minimum of the dataset
* @param absoluteMax Absolute maximum of the dataset
*/
public void setData(float min, float lowerQuartile, float average,
float upperQuartile, float max,
float absoluteMin, float absoluteMax) {
this.min = min;
this.lowerQuartile = lowerQuartile;
this.average = average;
this.upperQuartile = upperQuartile;
this.max = max;
this.absoluteMin = absoluteMin;
this.absoluteMax = absoluteMax;
// Request redraw
invalidate();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int desiredWidth = 600;
int desiredHeight = CANDLESTICK_HEIGHT + PADDING_TOP + PADDING_BOTTOM;
int width = resolveSize(desiredWidth, widthMeasureSpec);
int height = resolveSize(desiredHeight, heightMeasureSpec);
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
// Calculate scaling factor for data points
float availableWidth = width - PADDING_LEFT - PADDING_RIGHT;
float scale = availableWidth / (absoluteMax - absoluteMin);
// Calculate vertical center of the view
int centerY = height / 2;
// Calculate scaled positions
float scaledMin = PADDING_LEFT + (min - absoluteMin) * scale;
float scaledLQ = PADDING_LEFT + (lowerQuartile - absoluteMin) * scale;
float scaledAvg = PADDING_LEFT + (average - absoluteMin) * scale;
float scaledUQ = PADDING_LEFT + (upperQuartile - absoluteMin) * scale;
float scaledMax = PADDING_LEFT + (max - absoluteMin) * scale;
// Draw the box (interquartile range)
if(lowerQuartile != upperQuartile) {
RectF box = new RectF(
scaledLQ,
centerY - CANDLESTICK_HEIGHT / 2,
scaledUQ,
centerY + CANDLESTICK_HEIGHT / 2);
canvas.drawRect(box, boxPaint);
}
// Draw whiskers (min to lower quartile and upper quartile to max)
// Left whisker
canvas.drawLine(scaledMin, centerY, scaledLQ, centerY, whiskerPaint);
canvas.drawLine(
scaledMin,
centerY - WHISKER_HEIGHT,
scaledMin,
centerY + WHISKER_HEIGHT,
whiskerPaint);
// Right whisker
canvas.drawLine(scaledUQ, centerY, scaledMax, centerY, whiskerPaint);
canvas.drawLine(
scaledMax,
centerY - WHISKER_HEIGHT,
scaledMax,
centerY + WHISKER_HEIGHT,
whiskerPaint);
// Draw average line
canvas.drawLine(
scaledAvg,
centerY - CANDLESTICK_HEIGHT / 2,
scaledAvg,
centerY + CANDLESTICK_HEIGHT / 2,
averagePaint);
// Draw labels
// canvas.drawText(String.format("%.1f", min), scaledMin - 20, centerY - CANDLESTICK_HEIGHT / 2 - 10, textPaint);
// canvas.drawText(String.format("%.1f", max), scaledMax - 20, centerY - CANDLESTICK_HEIGHT / 2 - 10, textPaint);
// canvas.drawText(String.format("%.1f", average), scaledAvg - 20, centerY + CANDLESTICK_HEIGHT / 2 + 30, textPaint);
}
public int teamNum;
public void fromTeamData(List<RawDataType> teamData, Integer teamNum, float absmin, float absmax){
this.teamNum = teamNum;
int[] tmp_loc_bounds = DataProcessing.getNumberBounds(teamData);
int locmin = tmp_loc_bounds[0];
int locmax = tmp_loc_bounds[1];
float avg = 0;
for(int i = 0; i < teamData.size(); i++){
avg += (int) teamData.get(i).get();
}
avg /= teamData.size();
float[] teamDataArray = new float[teamData.size()];
for(int i = 0; i < teamData.size(); i++){
teamDataArray[i] = (int) teamData.get(i).get();
}
float lowerQuartile = 0;
float upperQuartile = 0;
if(teamDataArray.length != 0) {
lowerQuartile = DataProcessing.calculatePercentile(teamDataArray, 25);
upperQuartile = DataProcessing.calculatePercentile(teamDataArray, 75);
}
Log.i(getClass().toString(), locmin + ", " + lowerQuartile + ", " + avg + ", " + upperQuartile + ", " + locmax);
setData(locmin, lowerQuartile, avg, upperQuartile, locmax, absmin, absmax);
}
}
@@ -0,0 +1,105 @@
package com.ridgebotics.ridgescout.ui.views;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.CheckBox;
import android.widget.TableLayout;
import androidx.annotation.Nullable;
import com.google.android.material.divider.MaterialDivider;
import java.util.List;
// The notification view of a Custom dropdown notification
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,123 @@
package com.ridgebotics.ridgescout.ui.views;
import static android.app.PendingIntent.getActivity;
import android.app.AlertDialog;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import androidx.annotation.Nullable;
import com.ridgebotics.ridgescout.R;
import java.util.List;
// The dropdown element of a custom spinner view.
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,66 @@
package com.ridgebotics.ridgescout.ui.views;
import android.content.Context;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TableRow;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.types.input.FieldType;
public class FieldBorderedRow extends TableRow {
public FieldBorderedRow(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public FieldBorderedRow(Context context) {
super(context);
init(context);
}
private ConstraintLayout box;
private View coloredBackground;
public void init(Context context) {
LayoutInflater.from(context).inflate(R.layout.view_field_border_row, this, true);
box = findViewById(R.id.field_option_box);
coloredBackground = findViewById(R.id.field_option_background);
}
public void setColor(int color){
Drawable drawable = box.getBackground();
drawable.mutate();
drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
float[] hsv = new float[3];
Color.colorToHSV(color,hsv);
coloredBackground.setBackgroundColor(
Color.HSVToColor(127, new float[]{
hsv[0],
Math.min(hsv[1], 0.75f),
Math.min(hsv[2], 0.5f)
})
);
}
public void fromField(FieldType field){
((TextView) findViewById(R.id.field_option_name)).setText(field.name);
((TextView) findViewById(R.id.field_option_type)).setText(field.get_type_name());
}
@Override
public void addView(View v){
box.addView(v);
}
}
@@ -0,0 +1,119 @@
package com.ridgebotics.ridgescout.ui.views;
import android.content.Context;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.types.input.FieldType;
// Shows a display of the view in the view editor.
public class FieldDisplay extends ConstraintLayout {
public FieldDisplay(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public FieldDisplay(Context context) {
super(context);
init(context);
}
private FieldType field;
public Button editButton;
// private View fieldView;
private ConstraintLayout box;
public View coloredBackground;
private LinearLayout fieldDisplayBox;
public void init(Context context) {
LayoutInflater.from(context).inflate(R.layout.view_field_display, this, true);
box = findViewById(R.id.field_box);
fieldDisplayBox = findViewById(R.id.field_display_box);
coloredBackground = findViewById(R.id.field_background);
editButton = findViewById(R.id.button_edit);
}
private ToggleTitleView toggleTitleView;
private View fieldView;
public void setField(FieldType field){
this.field = field;
// titleText.setText(field.name);
// typeText.setText(field.get_type_name());
toggleTitleView = new ToggleTitleView(getContext());
toggleTitleView.setTitle(field.name);
toggleTitleView.setDescription(field.description);
fieldView = field.createView(getContext(), dataType -> 0);
fieldDisplayBox.removeAllViews();
fieldDisplayBox.addView(toggleTitleView);
fieldDisplayBox.addView(fieldView);
}
// public void setField(View newView){
// if(fieldView != null)
// box.removeView(fieldView);
// box.addView(newView);
// fieldView = newView;
// }
// public void fromTeam(frcTeam team){
// setTeamNumber(team.teamNumber);
// setTeamName(team.teamName);
//
// if(team.bitmap != null) {
// setTeamLogo(team.bitmap);
// }else{
// hideLogo();
// }
//
// setColor(team.getTeamColor());
// }
public void showButtons(){
findViewById(R.id.buttons).setVisibility(View.VISIBLE);
}
public void hideButtons(){
findViewById(R.id.buttons).setVisibility(View.GONE);
}
public FieldType getField(){
return field;
}
public void setColor(int color){
Drawable drawable = box.getBackground();
drawable.mutate();
drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
float[] hsv = new float[3];
Color.colorToHSV(color,hsv);
coloredBackground.setBackgroundColor(
Color.HSVToColor(127, new float[]{
hsv[0],
Math.min(hsv[1] - 0.25f, 0.75f),
Math.min(hsv[2] - 0.25f, 0.5f)
})
);
}
}
@@ -1,4 +1,4 @@
package com.ridgebotics.ridgescout.ui.scouting; package com.ridgebotics.ridgescout.ui.views;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
@@ -13,7 +13,10 @@ import android.widget.FrameLayout;
import android.widget.ImageView; import android.widget.ImageView;
import com.ridgebotics.ridgescout.R; import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.types.input.FieldposType;
import com.ridgebotics.ridgescout.utility.SettingsManager;
// Custom view to display a field position.
public class FieldPosView extends FrameLayout { public class FieldPosView extends FrameLayout {
private int x = -1; private int x = -1;
private int y = -1; private int y = -1;
@@ -22,6 +25,8 @@ public class FieldPosView extends FrameLayout {
private ImageView imageView; private ImageView imageView;
private boolean enabled = true; private boolean enabled = true;
private boolean flip = false;
public interface onTapListener { public interface onTapListener {
void onUpdate(int[] pos); void onUpdate(int[] pos);
}; };
@@ -63,17 +68,21 @@ public class FieldPosView extends FrameLayout {
// Set touch listener // Set touch listener
setOnTouchListener((v, event) -> { setOnTouchListener((v, event) -> {
if (enabled && event.getAction() == MotionEvent.ACTION_DOWN) { if (enabled && event.getAction() == MotionEvent.ACTION_DOWN) {
x = (int) ((event.getX()/getWidth())*255); // If the field image is rotated, rotate the input and output
y = (int) ((event.getY()/getHeight())*255); if(!flip) {
x = (int) (event.getX() / getWidth()) * 255;
y = (int) (event.getY() / getHeight()) * 255;
} else {
x = (int) (1 - (event.getX() / getWidth())) * 255;
y = (int) (1 - (event.getY() / getHeight())) * 255;
}
System.out.println("X: " + x + ", Y: " + y);
tl.onUpdate(getPos()); tl.onUpdate(getPos());
invalidate(); invalidate();
return true; return true;
} }
return false; return false;
}); });
setImageResource(R.drawable.field_2024);
} }
public void setPos(int[] pos){ public void setPos(int[] pos){
@@ -87,10 +96,28 @@ public class FieldPosView extends FrameLayout {
protected void dispatchDraw(Canvas canvas) { protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas); super.dispatchDraw(canvas);
if (x >= 0 && y >= 0) { if (x >= 0 && y >= 0) {
canvas.drawCircle(
((float) x /255)*getWidth(), float cx;
((float) y /255)*getHeight(), float cy;
POINT_RADIUS, paint); // If the field image is rotated, rotate the input and output
if(!flip) {
cx = ((float) x / 255)*getWidth();
cy = ((float) y / 255)*getHeight();
} else {
cx = (1 - (float) x / 255)*getWidth();
cy = (1 - (float) y /255)*getHeight();
}
canvas.drawCircle(cx, cy, POINT_RADIUS, paint);
}
}
public void setFieldImage(FieldposType.FieldImage image) {
if(image.flippable && SettingsManager.getFieldImageFlipped()) {
setImageResource(image.resId_flipped);
flip = true;
} else {
setImageResource(image.resId_normal);
} }
} }
@@ -0,0 +1,118 @@
package com.ridgebotics.ridgescout.ui.views;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.types.frcTeam;
import org.w3c.dom.Text;
// A view for displaying information about a team.
public class MatchScoutingIndicator extends RelativeLayout {
public MatchScoutingIndicator(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public MatchScoutingIndicator(Context context) {
super(context);
init(context);
}
public TextView match_indicator_alliance_pos_text;
public TextView match_indicator_bar_team_num;
public TextView match_indicator_matchnum;
public TextView match_indicator_username;
public ImageButton match_indicator_back_button;
public ImageButton match_indicator_next_button;
private ConstraintLayout box;
private View coloredBackground;
public void init(Context context) {
LayoutInflater.from(context).inflate(R.layout.view_match_scouting_indicator, this, true);
match_indicator_back_button = findViewById(R.id.match_indicator_back_button);
match_indicator_next_button = findViewById(R.id.match_indicator_next_button);
match_indicator_alliance_pos_text = findViewById(R.id.match_indicator_alliance_pos_text);
match_indicator_username = findViewById(R.id.match_indicator_username);
match_indicator_matchnum = findViewById(R.id.match_indicator_matchnum);
match_indicator_bar_team_num = findViewById(R.id.match_indicator_bar_team_num);
box = findViewById(R.id.file_indicator_box);
coloredBackground = findViewById(R.id.match_indicator_background);
int currentNightMode = getContext().getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
switch (currentNightMode) {
case Configuration.UI_MODE_NIGHT_NO:
// Night mode is not active on device
match_indicator_back_button.setColorFilter(Color.BLACK, PorterDuff.Mode.SRC_IN);
match_indicator_next_button.setColorFilter(Color.BLACK, PorterDuff.Mode.SRC_IN);
break;
case Configuration.UI_MODE_NIGHT_YES:
// Night mode is active on device
match_indicator_back_button.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN);
match_indicator_next_button.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN);
break;
}
}
public void setUsername(String username){
match_indicator_username.setText(username);
}
public void setAlliancePos(String alliancePos){
match_indicator_alliance_pos_text.setText(alliancePos);
}
public void setMatchNum(String matchNum){
match_indicator_matchnum.setText(matchNum);
}
public void setTeamNum(String teamNum){
match_indicator_bar_team_num.setText(teamNum);
}
public void setColor(int color){
// Set color of main background rectangle
Drawable box_drawable = box.getBackground();
box_drawable.mutate();
box_drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
float[] hsv = new float[3];
Color.colorToHSV(color,hsv);
int background_color = Color.HSVToColor(220, new float[]{
hsv[0],
Math.min(hsv[1], 0.75f),
Math.min(hsv[2], 0.5f)
});
// Set color of main background rectangle, slightly dimmer
coloredBackground.setBackgroundColor(
background_color
);
Drawable left_drawable = match_indicator_back_button.getBackground();
left_drawable.mutate();
left_drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
Drawable right_drawable = match_indicator_next_button.getBackground();
right_drawable.mutate();
right_drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
}
}
@@ -1,18 +1,17 @@
package com.ridgebotics.ridgescout.ui.scouting; package com.ridgebotics.ridgescout.ui.views;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.ImageView; import android.widget.ImageView;
import com.ridgebotics.ridgescout.R; import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.types.input.FieldposType;
import com.ridgebotics.ridgescout.utility.SettingsManager;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -51,9 +50,6 @@ public class MultiFieldPosView extends FrameLayout {
imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE); imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
imageView.setAdjustViewBounds(true); imageView.setAdjustViewBounds(true);
addView(imageView); addView(imageView);
setImageResource(R.drawable.field_2024);
} }
public void addPos(int[] pos){ public void addPos(int[] pos){
@@ -65,6 +61,14 @@ public class MultiFieldPosView extends FrameLayout {
invalidate(); invalidate();
} }
public void setFieldImage(FieldposType.FieldImage image) {
if(image.flippable && SettingsManager.getFieldImageFlipped()) {
setImageResource(image.resId_flipped);
} else {
setImageResource(image.resId_normal);
}
}
@Override @Override
protected void dispatchDraw(Canvas canvas) { protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas); super.dispatchDraw(canvas);
@@ -0,0 +1,75 @@
package com.ridgebotics.ridgescout.ui.views;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageButton;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.ridgebotics.ridgescout.R;
// A view for displaying information about a team.
public class PitScoutingIndicator extends RelativeLayout {
public PitScoutingIndicator(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public PitScoutingIndicator(Context context) {
super(context);
init(context);
}
public TextView pit_indicator_username;
public TextView pit_indicator_team_num;
private ConstraintLayout box;
private View coloredBackground;
public void init(Context context) {
LayoutInflater.from(context).inflate(R.layout.view_pit_scouting_indicator, this, true);
pit_indicator_username = findViewById(R.id.pit_indicator_username);
pit_indicator_team_num = findViewById(R.id.pit_indicator_teamnum);
box = findViewById(R.id.pit_indicator_box);
coloredBackground = findViewById(R.id.pit_indicator_background);
}
public void setUsername(String username){
pit_indicator_username.setText(username);
}
public void setTeamNum(int teamNum) {
pit_indicator_team_num.setText(String.valueOf(teamNum));
}
public void setColor(int color){
// Set color of main background rectangle
Drawable box_drawable = box.getBackground();
box_drawable.mutate();
box_drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
float[] hsv = new float[3];
Color.colorToHSV(color,hsv);
int background_color = Color.HSVToColor(220, new float[]{
hsv[0],
Math.min(hsv[1], 0.75f),
Math.min(hsv[2], 0.5f)
});
// Set color of main background rectangle, slightly dimmer
coloredBackground.setBackgroundColor(
background_color
);
}
}
@@ -0,0 +1,90 @@
package com.ridgebotics.ridgescout.ui.views;
import android.content.Context;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.ridgebotics.ridgescout.R;
// Class for custom view displaying an event in the TBASelector.java
public class TBAEventOption extends LinearLayout {
public TBAEventOption(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public TBAEventOption(Context context) {
super(context);
init(context);
}
private TextView eventCode;
private TextView eventName;
private TextView eventType;
private ConstraintLayout box;
private View coloredBackground;
public void init(Context context) {
LayoutInflater.from(context).inflate(R.layout.view_tba_event, this, true);
eventCode = findViewById(R.id.tba_event_code);
eventName = findViewById(R.id.tba_event_name);
eventType = findViewById(R.id.tba_event_type);
box = findViewById(R.id.tba_event_box);
coloredBackground = findViewById(R.id.tba_event_background);
}
public void setCode(String code){
eventCode.setText(String.valueOf(code));
}
public void setName(String name){
eventName.setText(name);
}
public void setType(String name){
eventType.setText(name);
}
// public void fromTeam(frcTeam team){
// setTeamNumber(team.teamNumber);
// setTeamName(team.teamName);
//
// if(team.bitmap != null) {
// setTeamLogo(team.bitmap);
// }else{
// hideLogo();
// }
//
// setColor(team.getTeamColor());
// }
public void setColor(int color){
Drawable drawable = box.getBackground();
drawable.mutate();
drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
float[] hsv = new float[3];
Color.colorToHSV(color,hsv);
coloredBackground.setBackgroundColor(
Color.HSVToColor(127, new float[]{
hsv[0],
Math.min(hsv[1], 0.75f),
Math.min(hsv[2], 0.5f)
})
);
}
}
@@ -1,4 +1,4 @@
package com.ridgebotics.ridgescout.ui.scouting; package com.ridgebotics.ridgescout.ui.views;
import android.content.Context; import android.content.Context;
import android.util.AttributeSet; import android.util.AttributeSet;
@@ -9,12 +9,15 @@ import android.widget.TextView;
import com.ridgebotics.ridgescout.R; import com.ridgebotics.ridgescout.R;
// Unused. This would be almost the same as the Fieldpos field, except it would support multiple positions.
public class TallyCounterView extends LinearLayout { public class TallyCounterView extends LinearLayout {
private int count = 0; private int count = 0;
private TextView countDisplay; private TextView countDisplay;
private Button minusButton; private Button minusButton;
private Button plusButton; private Button plusButton;
private OnCountChangedListener onCountChangedListener; private OnCountChangedListener onCountChangedListener;
private int min = 0;
private int max = -1;
public interface OnCountChangedListener { public interface OnCountChangedListener {
void onCountChanged(int newCount); void onCountChanged(int newCount);
@@ -45,20 +48,26 @@ public class TallyCounterView extends LinearLayout {
updateDisplay(); updateDisplay();
minusButton.setOnClickListener(v -> { minusButton.setOnClickListener(v -> {
if(count > 0) { if(count > min) {
count--; count--;
updateDisplay(); updateDisplay();
} }
}); });
plusButton.setOnClickListener(v -> { plusButton.setOnClickListener(v -> {
if(max == -1 || count < max){
count++; count++;
updateDisplay(); updateDisplay();
}
}); });
} }
private void updateDisplay() { private void updateDisplay() {
countDisplay.setText(String.valueOf(count)); countDisplay.setText(String.valueOf(count));
minusButton.setEnabled(count > min);
plusButton.setEnabled(max == -1 || count < max);
if (onCountChangedListener != null) { if (onCountChangedListener != null) {
onCountChangedListener.onCountChanged(count); onCountChangedListener.onCountChanged(count);
} }
@@ -69,6 +78,12 @@ public class TallyCounterView extends LinearLayout {
updateDisplay(); updateDisplay();
} }
public void setBounds(int min, int max){
this.min = min;
this.max = max;
updateDisplay();
}
public int getValue() { public int getValue() {
return count; return count;
} }
@@ -0,0 +1,109 @@
package com.ridgebotics.ridgescout.ui.views;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.types.frcTeam;
// A view for displaying information about a team.
public class TeamCard extends LinearLayout {
public TeamCard(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public TeamCard(Context context) {
super(context);
init(context);
}
private TextView teamNumber;
private TextView teamName;
private TextView teamDescription;
private ImageView teamLogo;
private ConstraintLayout box;
private View coloredBackground;
public void init(Context context) {
LayoutInflater.from(context).inflate(R.layout.view_team_card, this, true);
teamNumber = findViewById(R.id.team_card_number);
teamName = findViewById(R.id.team_card_name);
teamLogo = findViewById(R.id.team_card_logo);
teamDescription = findViewById(R.id.team_card_description);
box = findViewById(R.id.team_card_box);
coloredBackground = findViewById(R.id.team_card_background);
}
public void setTeamNumber(int num){
teamNumber.setText(String.valueOf(num));
}
public void setTeamName(String name){
teamName.setText(name);
}
public void setTeamLogo(Bitmap bitmap){
teamLogo.setImageBitmap(bitmap);
}
public void setTeamDescription(String description){
teamDescription.setText(description);
}
public void hideLogo(){
teamLogo.setVisibility(View.GONE);
}
public void showLogo(){
teamLogo.setVisibility(View.VISIBLE);
}
public void fromTeam(frcTeam team){
setTeamNumber(team.teamNumber);
setTeamName(team.teamName);
if(team.bitmap != null) {
showLogo();
setTeamLogo(team.bitmap);
}else{
hideLogo();
}
setTeamDescription(team.getDescription());
setColor(team.getTeamColor());
}
public void setColor(int color){
Drawable drawable = box.getBackground();
drawable.mutate();
drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
float[] hsv = new float[3];
Color.colorToHSV(color,hsv);
coloredBackground.setBackgroundColor(
Color.HSVToColor(127, new float[]{
hsv[0],
Math.min(hsv[1], 0.75f),
Math.min(hsv[2], 0.5f)
})
);
}
}
@@ -0,0 +1,100 @@
package com.ridgebotics.ridgescout.ui.views;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.types.frcTeam;
// A view that acts as a row specifically to display a team and their icon in a list formmt.
public class TeamListOption extends LinearLayout {
public TeamListOption(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public TeamListOption(Context context) {
super(context);
init(context);
}
private TextView teamNumber;
private TextView teamName;
private ImageView teamLogo;
private ConstraintLayout box;
private View coloredBackground;
public void init(Context context) {
LayoutInflater.from(context).inflate(R.layout.view_team_option, this, true);
teamNumber = findViewById(R.id.field_option_type);
teamName = findViewById(R.id.field_option_name);
teamLogo = findViewById(R.id.team_option_logo);
box = findViewById(R.id.team_option_box);
coloredBackground = findViewById(R.id.team_option_background);
}
public void setTeamNumber(int num){
teamNumber.setText(String.valueOf(num));
}
public void setTeamName(String name){
teamName.setText(name);
}
public void setTeamLogo(Bitmap bitmap){
teamLogo.setImageBitmap(bitmap);
}
public void hideLogo(){
teamLogo.setVisibility(View.GONE);
}
public void showLogo(){
teamLogo.setVisibility(View.VISIBLE);
}
public void fromTeam(frcTeam team){
setTeamNumber(team.teamNumber);
setTeamName(team.teamName);
if(team.bitmap != null) {
setTeamLogo(team.bitmap);
}else{
hideLogo();
}
setColor(team.getTeamColor());
}
public void setColor(int color){
Drawable drawable = box.getBackground();
drawable.mutate();
drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
float[] hsv = new float[3];
Color.colorToHSV(color,hsv);
coloredBackground.setBackgroundColor(
Color.HSVToColor(127, new float[]{
hsv[0],
Math.min(hsv[1], 0.75f),
Math.min(hsv[2], 0.5f)
})
);
}
}
@@ -0,0 +1,101 @@
package com.ridgebotics.ridgescout.ui.views;
import static com.ridgebotics.ridgescout.utility.Colors.toggletitle_black_background;
import static com.ridgebotics.ridgescout.utility.Colors.toggletitle_no_background;
import static com.ridgebotics.ridgescout.utility.Colors.toggletitle_unselected;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.CheckBox;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.ridgebotics.ridgescout.R;
// The enable and disable button for match and pit scouting
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();
if(onToggleListener != null)
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(toggletitle_unselected);
titleView.setTextColor(toggletitle_black_background);
}
public void enable(){
enabled = true;
toggle_title_checkbox.setChecked(true);
toggle_title_description.setVisibility(View.VISIBLE);
setBackgroundColor(toggletitle_no_background);
titleView.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Headline5);
}
public void setEnabled(boolean enabled){
if(enabled)
disable();
else
enable();
}
}
@@ -1,71 +1,134 @@
package com.ridgebotics.ridgescout.utility; package com.ridgebotics.ridgescout.utility;
import android.annotation.SuppressLint;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface;
import android.widget.Toast; import android.widget.Toast;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
// Class to cause alerts and popups
public class AlertManager { public class AlertManager {
@SuppressLint("StaticFieldLeak")
public static Context context; public static Context context;
public static void init(Context c){ public static void init(Context c){
context = c; context = c;
} }
private static AlertDialog currentError;
private static final List<String> simpleErrorList = new ArrayList<>();
private static final List<String> errorList = new ArrayList<>();
public static void alert(String title, String content) { public static void alert(String title, String content) {
((Activity) context).runOnUiThread(new Runnable() {
public void run() { ((Activity) context).runOnUiThread(() -> {
AlertDialog.Builder alert = new AlertDialog.Builder(context); AlertDialog.Builder alert = new AlertDialog.Builder(context);
alert.setMessage(content);
alert.setTitle(title); alert.setTitle(title);
alert.setMessage(content);
alert.setPositiveButton("OK", null); alert.setPositiveButton("OK", null);
alert.setCancelable(true); alert.setCancelable(true);
alert.create().show(); alert.create().show();
}
}); });
} }
public static void toast(String content) { public static void toast(String content) {
((Activity) context).runOnUiThread(new Runnable() { ((Activity) context).runOnUiThread(() -> Toast.makeText(context, content, Toast.LENGTH_LONG).show());
public void run() {
Toast.makeText(context, content, Toast.LENGTH_LONG).show();
} }
});
public static void addSimpleError(String error) {
simpleErrorList.add(error);
updateErrors();
} }
public static void error(String content) { public static void error(String content) {
((Activity) context).runOnUiThread(new Runnable() { errorList.add(content);
public void run() { updateErrors();
AlertDialog.Builder alert = new AlertDialog.Builder(context);
alert.setMessage(content);
alert.setTitle("Error!");
alert.setPositiveButton("OK", null);
alert.setCancelable(true);
alert.create().show();
} }
});
public static void error(String title, String content) {
simpleErrorList.add(title);
errorList.add(content);
updateErrors();
} }
public static void error(Exception e) { public static void error(Exception e) {
e.printStackTrace(); e.printStackTrace();
((Activity) context).runOnUiThread(new Runnable() { // simpleErrorList.add(e.getMessage());
public void run() {
StringWriter sw = new StringWriter(); StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw)); e.printStackTrace(new PrintWriter(sw));
AlertDialog.Builder alert = new AlertDialog.Builder(context); errorList.add(sw.getBuffer().toString());
alert.setMessage(sw.toString()); updateErrors();
alert.setTitle(e.getMessage());
alert.setPositiveButton("OK", null);
alert.setCancelable(true);
alert.create().show();
} }
public static void error(String title, Exception e) {
simpleErrorList.add(title);
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
errorList.add(sw.toString());
e.printStackTrace();
updateErrors();
}
public static void updateErrors(){
((Activity) context).runOnUiThread(() -> {
if(currentError != null && currentError.isShowing()){
DialogInterface tmp = currentError;
currentError = null;
tmp.dismiss();
}
AlertDialog.Builder alert = new AlertDialog.Builder(context);
if(!simpleErrorList.isEmpty())
alert.setTitle(simpleErrorList.get(0) + (simpleErrorList.size() > 1 ? "..." : ""));
else
alert.setTitle(errorList.size() + " Error" + (errorList.size() != 1 ? "s" : "") + "!");
if(simpleErrorList.size() > 1)
alert.setMessage(String.join("\n", simpleErrorList));
alert.setPositiveButton("OK", (dialogInterface, i) -> {if(currentError != null){errorList.clear(); simpleErrorList.clear();}});
String detailedErrors = String.join("\n\n\n\n\n", errorList);
if(!errorList.isEmpty())
alert.setNeutralButton("View Detailed Error" + (errorList.size() != 1 ? "s" : ""), (dialogInterface, i) -> alert("Details", detailedErrors));
// alert.setOnDismissListener((x) -> {if(currentError != null){errorList.clear(); simpleErrorList.clear();}});
alert.setCancelable(true);
currentError = alert.create();
currentError.show();
});
}
private static ProgressDialog loadingDialog;
public static void startLoading(String title){
((Activity) context).runOnUiThread(() -> {
if(loadingDialog != null && loadingDialog.isShowing())
loadingDialog.dismiss();
loadingDialog = ProgressDialog.show(context, title, "Please wait...");
});
}
public static void stopLoading(){
((Activity) context).runOnUiThread(() -> {
if (loadingDialog != null)
loadingDialog.cancel();
loadingDialog = null;
}); });
} }
@@ -3,14 +3,17 @@ package com.ridgebotics.ridgescout.utility;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
// Class to run a save function after a timeout of not calling the update function has been reached.
// Used for auto-saving in pit and match scouting.
public class AutoSaveManager { public class AutoSaveManager {
private static final long AUTO_SAVE_DELAY = 2000; // 2 seconds public static final long AUTO_SAVE_DELAY = 2000; // 2 seconds
private final Handler handler; private final Handler handler;
private final Runnable autoSaveRunnable; private final Runnable autoSaveRunnable;
private boolean isAutoSaveScheduled = false; private boolean isAutoSaveScheduled = false;
private final AutoSaveFunction autoSaveFunction; private final AutoSaveFunction autoSaveFunction;
public boolean isRunning = false; public boolean isRunning = false;
private long delay;
// Functional interface for the auto-save function // Functional interface for the auto-save function
@FunctionalInterface @FunctionalInterface
@@ -18,8 +21,9 @@ public class AutoSaveManager {
void save(); void save();
} }
public AutoSaveManager(AutoSaveFunction autoSaveFunction) { public AutoSaveManager(AutoSaveFunction autoSaveFunction, long delay) {
this.autoSaveFunction = autoSaveFunction; this.autoSaveFunction = autoSaveFunction;
this.delay = delay;
handler = new Handler(Looper.getMainLooper()); handler = new Handler(Looper.getMainLooper());
autoSaveRunnable = new Runnable() { autoSaveRunnable = new Runnable() {
@Override @Override
@@ -1,17 +1,20 @@
package com.ridgebotics.ridgescout.utility; package com.ridgebotics.ridgescout.utility;
import static com.ridgebotics.ridgescout.utility.FileEditor.lengthHeaderBytes;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
// Class to decode the raw types from any of the scouting files
public class BuiltByteParser { public class BuiltByteParser {
public static final Integer boolType = 0; public static final Integer boolType = 0;
public static final Integer intType = 1; public static final Integer intType = 1;
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 static class byteParsingExeption extends Exception {
public byteParsingExeption() {} public byteParsingExeption() {}
public byteParsingExeption(String message) { public byteParsingExeption(String message) {
super(message); super(message);
@@ -58,6 +61,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 {
@@ -70,26 +78,36 @@ public class BuiltByteParser {
byte[] bytes; byte[] bytes;
ArrayList<parsedObject> objects = new ArrayList<>(); ArrayList<parsedObject> objects = new ArrayList<>();
public BuiltByteParser(byte[] bytes){ public BuiltByteParser(byte[] bytes){
this.bytes = bytes; this.bytes = bytes;
} }
public ArrayList<parsedObject> parse() throws byteParsingExeption { public ArrayList<parsedObject> parse() throws byteParsingExeption {
if(bytes.length < 3){throw new byteParsingExeption("Invalid length");} try{
return parse(lengthHeaderBytes); // Try decoding with new format
}catch (byteParsingExeption e){
objects.clear(); // Clear previous attempt at decoding
return parse(lengthHeaderBytes - 1); // Try parsing with old format
}
}
private ArrayList<parsedObject> parse(final int size) throws byteParsingExeption {
if(bytes.length < size + 1){throw new byteParsingExeption("Invalid length");}
int curIndex = 0; int curIndex = 0;
while(true){ while(true){
// Log.i("t", String.valueOf(curIndex)); // Log.i("t", String.valueOf(curIndex));
final int length = fileEditor.fromBytes(fileEditor.getByteBlock(bytes, curIndex, curIndex+2), 2); final int length = FileEditor.fromBytes(FileEditor.getByteBlock(bytes, curIndex, curIndex+size), size);
final int type = bytes[curIndex+2] & 0xFF; final int type = bytes[curIndex+size] & 0xFF;
if(length == 0){ if(length == 0){
curIndex += 3; curIndex += size;
continue; continue;
} }
final byte[] block; final byte[] block;
try { try {
block = fileEditor.getByteBlock(bytes, curIndex + 3, curIndex + length + 3); block = FileEditor.getByteBlock(bytes, curIndex + size + 1, curIndex + length + size + 1);
} catch(Exception e){ } catch(Exception e){
throw new byteParsingExeption("Array out of bounds"); throw new byteParsingExeption("Array out of bounds");
} }
@@ -102,7 +120,7 @@ public class BuiltByteParser {
break; break;
case 1: case 1:
intObject io = new intObject(); intObject io = new intObject();
io.num = fileEditor.fromBytes(block, length); io.num = FileEditor.fromBytes(block, length);
objects.add(io); objects.add(io);
break; break;
case 2: case 2:
@@ -122,7 +140,7 @@ public class BuiltByteParser {
intArrayObject ia = new intArrayObject(); intArrayObject ia = new intArrayObject();
ia.arr = intArr; ia.arr = intArr;
System.out.println(Arrays.toString(intArr)); // System.out.println(Arrays.toString(intArr));lengthHeaderBytes
objects.add(ia); objects.add(ia);
break; break;
case 4: case 4:
@@ -140,6 +158,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;
@@ -147,7 +170,7 @@ public class BuiltByteParser {
break; break;
} }
curIndex += length + 3; curIndex += length + size + 1;
if(curIndex == bytes.length){ if(curIndex == bytes.length){
break; break;
@@ -1,14 +1,18 @@
package com.ridgebotics.ridgescout.utility; package com.ridgebotics.ridgescout.utility;
import static com.ridgebotics.ridgescout.utility.FileEditor.lengthHeaderBytes;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
// Class to encode the raw types from any of the scouting files
public class ByteBuilder { public class ByteBuilder {
public static final int bool_id = 0; public static final int bool_id = 0;
public static final int int_id = 1; public static final int int_id = 1;
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<>();
@@ -55,7 +59,7 @@ public class ByteBuilder {
public byte getType(){return int_id;} public byte getType(){return int_id;}
public int length(){return precision;} public int length(){return precision;}
public byte[] build(){ public byte[] build(){
return fileEditor.toBytes(num, precision); return FileEditor.toBytes(num, precision, lengthHeaderBytes);
} }
} }
private int getLeastBytePrecision(int num){ private int getLeastBytePrecision(int num){
@@ -70,7 +74,7 @@ public class ByteBuilder {
public ByteBuilder addInt(int num, int precision) throws buildingException { public ByteBuilder addInt(int num, int precision) throws buildingException {
if(precision <= 0){throw new buildingException("Invalid precision: " + precision);} if(precision <= 0){throw new buildingException("Invalid precision: " + precision);}
if(precision > 65536){throw new buildingException("Precision too large (greter than 65536)");} if(precision > Math.pow(lengthHeaderBytes,8)){throw new buildingException("Precision too large (greter than 16777216)");}
if(precision < getLeastBytePrecision(num)){throw new buildingException("Precision too small");} if(precision < getLeastBytePrecision(num)){throw new buildingException("Precision too small");}
if(num > Integer.MAX_VALUE){throw new buildingException("Integer overflow");} if(num > Integer.MAX_VALUE){throw new buildingException("Integer overflow");}
@@ -95,7 +99,7 @@ public class ByteBuilder {
} }
public ByteBuilder addString(String str) throws buildingException { public ByteBuilder addString(String str) throws buildingException {
str = blankStrNull(str); str = blankStrNull(str);
if(str.length() > 65536){throw new buildingException("String too long (greater than 65536)");} if(str.length() > Math.pow(lengthHeaderBytes,8)){throw new buildingException("String too long (greater than 16777216)");}
stringType stringType = new stringType(); stringType stringType = new stringType();
// To get the length correctly, the string bytes need to be precalculated // To get the length correctly, the string bytes need to be precalculated
@@ -153,6 +157,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, lengthHeaderBytes);
}
}
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 > Math.pow(lengthHeaderBytes,8)){throw new buildingException("Precision too large (greter than 16777216)");}
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;
@@ -164,7 +203,7 @@ public class ByteBuilder {
} }
public ByteBuilder addRaw(int type, byte[] bytes) throws buildingException { public ByteBuilder addRaw(int type, byte[] bytes) throws buildingException {
if(bytes.length > 65536){throw new buildingException("Byte array length to long (greater than 65536)");} if(bytes.length > 16777216){throw new buildingException("Byte array length to long (greater than 16777216)");}
rawType rawType = new rawType(); rawType rawType = new rawType();
rawType.type = type; rawType.type = type;
@@ -177,7 +216,7 @@ public class ByteBuilder {
public byte[] build() throws buildingException { public byte[] build() throws buildingException {
if(bytesToBuild.size() == 0){throw new buildingException("Cannot build null data");} if(bytesToBuild.size() == 0){throw new buildingException("Cannot build null data");}
int length = bytesToBuild.size() * 3; int length = bytesToBuild.size() * (lengthHeaderBytes + 1);
for(byteType bt : bytesToBuild){ for(byteType bt : bytesToBuild){
length += bt.length(); length += bt.length();
} }
@@ -187,12 +226,13 @@ public class ByteBuilder {
for(byteType bt : bytesToBuild){ for(byteType bt : bytesToBuild){
byte[] blockLength = fileEditor.toBytes(bt.length(), 2); byte[] blockLength = FileEditor.toBytes(bt.length(), lengthHeaderBytes + 1, lengthHeaderBytes);
bytes[bytesFilled] = blockLength[0]; for(int i = 0; i < lengthHeaderBytes; i++) {
bytesFilled += 1; bytes[bytesFilled] = blockLength[i];
bytes[bytesFilled] = blockLength[1];
bytesFilled += 1; bytesFilled += 1;
}
bytes[bytesFilled] = bt.getType(); bytes[bytesFilled] = bt.getType();
bytesFilled += 1; bytesFilled += 1;
@@ -0,0 +1,81 @@
package com.ridgebotics.ridgescout.utility;
import android.graphics.Color;
// Constants for most of the colors in the entire app.
public class Colors {
// Lists and stuff
public static final int color_found = 0x7f00ff00;
public static final int color_rescout = 0xff007fff;
public static final int color_not_found = 0x7f7f0000;
public static final int background_color = 0x5000ff00;
public static final int unfocused_background_color = 0x50118811;
public static final int unsaved_color = 0xffaa0000;
public static final int saved_color = 0xff00aa00;
public static final int rescout_color = 0xff007fff;
// Data graphs
public static final int chart_background = 0xff252025;
public static final int chart_text = Color.WHITE;
public static final int[] checkbox_colors = {0x7f00ff00, 0x7f7f0000};
public static final int checkbox_data_color = Color.RED;;
public static final int checkbox_value_text_color = Color.BLACK;
public static final int dropdown_value_text_1 = Color.BLACK;
public static final int dropdown_value_text_2 = Color.RED;
public static final int fieldpos_data = Color.BLUE;
public static final int number_data = Color.BLUE;
public static final int slider_data = Color.BLUE;
public static final int tally_data = Color.BLUE;
public static final int text_data = Color.BLUE;
// Code scanner
public static final int code_scanned_color = Color.GREEN;
public static final int code_selected_color = Color.YELLOW;
public static final int code_unscanned_color = Color.RED;
// Toggle title
public static final int toggletitle_no_background = 0x00000000;
public static final int toggletitle_black_background = 0xff000000;
public static final int toggletitle_unselected = 0xffff0000;
// public static final int toggletitle_no_background = 0x00000000;
// Data fragment
public static final int datafragment_option_1 = 0xff509050;
public static final int datafragment_option_2 = 0xff307030;
// fileselector
public static final int fileselector_selected_color = 0x5000ff00;
public static final int fileselector_unselected_color = 0x50006600;
// TBA
public static final int tba_previous = 0x30FF0000;
public static final int tba_current = 0x50ff0000;
public static final int tba_next = 0x30FFFF00;
public static final int tba_red = 0x50ff0000;
public static final int tba_blue = 0x500000ff;
public static final int tba_toggle_background = 0x30000000;
// Candlestick
public static final int candlestick_color = Color.GREEN;
}
@@ -1,37 +1,126 @@
package com.ridgebotics.ridgescout.utility; package com.ridgebotics.ridgescout.utility;
import com.ridgebotics.ridgescout.scoutingData.fields; import com.ridgebotics.ridgescout.scoutingData.Fields;
import com.ridgebotics.ridgescout.scoutingData.transfer.transferType; import com.ridgebotics.ridgescout.scoutingData.transfer.TransferType;
import com.ridgebotics.ridgescout.types.ColabArray;
import com.ridgebotics.ridgescout.types.frcEvent; import com.ridgebotics.ridgescout.types.frcEvent;
import com.ridgebotics.ridgescout.types.input.inputType; import com.ridgebotics.ridgescout.types.input.FieldType;
// Static class to hold loaded data, for ease of access.
public class DataManager { public class DataManager {
public static String evcode; public static String evcode;
public static frcEvent event; public static frcEvent event;
public static void reload_event(){ public static void reload_event(){
// if(event != null) return;
evcode = getevcode(); evcode = getevcode();
event = frcEvent.decode(fileEditor.readFile(evcode + ".eventdata"));
if(evcode.equals("unset")) return;
event = frcEvent.decode(FileEditor.readFile(evcode + ".eventdata"));
if(event == null) {
AlertManager.addSimpleError("Failed to load event!");
SettingsManager.setEVCode("unset");
evcode = "unset";
}else{
reload_rescout_list();
reload_scout_notice();
AlertManager.toast("Reloaded event!");
}
} }
public static String getevcode() { public static String getevcode() {
return settingsManager.getEVCode(); return SettingsManager.getEVCode();
} }
public static inputType[][] match_values; public static FieldType[][] match_values;
public static inputType[] match_latest_values; public static FieldType[] 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(){
match_values = fields.load(fields.matchFieldsFilename); try {
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("Error reading match fields", e);
}
} }
public static inputType[][] pit_values; public static FieldType[][] pit_values;
public static inputType[] pit_latest_values; public static FieldType[] 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(){
pit_values = fields.load(fields.pitsFieldsFilename); try {
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("Error reading pit fields", e);
}
}
public static ColabArray rescout_list = new ColabArray();
public static void reload_rescout_list(){
String filename = evcode + ".rescout";
if(!FileEditor.fileExist(filename)) {rescout_list = new ColabArray(); return;}
byte[] file = FileEditor.readFile(filename);
if(file == null) {rescout_list = new ColabArray(); return;}
try {
rescout_list = ColabArray.decode(file);
} catch (Exception e){
AlertManager.error("Error loading rescouting list", e);
rescout_list = new ColabArray();
}
}
public static void save_rescout_list() {
String filename = evcode + ".rescout";
try {
FileEditor.writeFile(filename, rescout_list.encode());
} catch (Exception e){
AlertManager.error("Error saving rescouting list", e);
}
}
public static String scoutNotice = "";
public static void reload_scout_notice(){
if(!FileEditor.fileExist(evcode + ".scoutnotice")) {scoutNotice = ""; return;}
byte[] file = FileEditor.readFile(evcode + ".scoutnotice");
if(file == null) {scoutNotice = ""; return;}
try {
BuiltByteParser bbp = new BuiltByteParser(file);
scoutNotice = (String) (bbp.parse().get(0).get());
} catch (Exception e){
AlertManager.error("Error loading scout notice", e);
scoutNotice = "";
}
}
public static void save_scout_notice() {
try {
if(scoutNotice.isEmpty()){
FileEditor.deleteFile(evcode + ".scoutnotice");
return;
}
ByteBuilder bb = new ByteBuilder();
bb.addString(scoutNotice);
FileEditor.writeFile(evcode + ".scoutnotice", bb.build());
} catch (Exception e){
AlertManager.error("Error saving scout notice", e);
}
} }
} }

Some files were not shown because too many files have changed in this diff Show More