diff --git a/.idea/AndroidProjectSystem.xml b/.idea/AndroidProjectSystem.xml
new file mode 100644
index 0000000..4a53bee
--- /dev/null
+++ b/.idea/AndroidProjectSystem.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
index b268ef3..f2d7cde 100644
--- a/.idea/deploymentTargetSelector.xml
+++ b/.idea/deploymentTargetSelector.xml
@@ -4,6 +4,14 @@
+
+
+
+
+
+
+
+
diff --git a/.idea/deviceManager.xml b/.idea/deviceManager.xml
new file mode 100644
index 0000000..91f9558
--- /dev/null
+++ b/.idea/deviceManager.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/planningMode.xml b/.idea/planningMode.xml
new file mode 100644
index 0000000..05fc08a
--- /dev/null
+++ b/.idea/planningMode.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index d899069..0ece391 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -37,6 +37,7 @@ dependencies {
implementation(libs.material)
implementation(libs.activity)
implementation(libs.constraintlayout)
+ implementation(libs.mpandroidchart)
testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit)
androidTestImplementation(libs.espresso.core)
diff --git a/app/release/app-release.apk b/app/release/app-release.apk
index ad9b2cf..02a6653 100644
Binary files a/app/release/app-release.apk and b/app/release/app-release.apk differ
diff --git a/app/release/baselineProfiles/0/app-release.dm b/app/release/baselineProfiles/0/app-release.dm
index ecc351b..157bea5 100644
Binary files a/app/release/baselineProfiles/0/app-release.dm and b/app/release/baselineProfiles/0/app-release.dm differ
diff --git a/app/release/baselineProfiles/1/app-release.dm b/app/release/baselineProfiles/1/app-release.dm
index 6e10463..8fb9fa3 100644
Binary files a/app/release/baselineProfiles/1/app-release.dm and b/app/release/baselineProfiles/1/app-release.dm differ
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 9631586..7f7c15f 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,6 +1,13 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/example/roboticsscoutingmatchapp/ActivityCompetitionSelection.java b/app/src/main/java/com/example/roboticsscoutingmatchapp/ActivityCompetitionSelection.java
index df07a7c..2c535fa 100644
--- a/app/src/main/java/com/example/roboticsscoutingmatchapp/ActivityCompetitionSelection.java
+++ b/app/src/main/java/com/example/roboticsscoutingmatchapp/ActivityCompetitionSelection.java
@@ -4,16 +4,22 @@
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
+import android.view.View;
import android.widget.Button;
-import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Toast;
import androidx.activity.EdgeToEdge;
+import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
import androidx.core.graphics.Insets;
+import androidx.core.view.GravityCompat;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
+import androidx.drawerlayout.widget.DrawerLayout;
+
+import com.google.android.material.navigation.NavigationView;
import java.io.BufferedReader;
import java.io.File;
@@ -24,6 +30,10 @@
import java.io.InputStreamReader;
import java.util.Calendar;
+/**
+ * Launcher activity for the app. Handles competition selection and data persistence checks.
+ * Serves as the home screen with navigation to the Data Dashboard.
+ */
public class ActivityCompetitionSelection extends AppCompatActivity {
@Override
@@ -31,19 +41,45 @@ protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_competition_selection);
- ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
+
+ // Sidebar/Navigation Drawer Setup
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ NavigationView navigationView = findViewById(R.id.nav_view);
+
+ // Adjust for system status bars
+ ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.app_bar), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
- v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
+ v.setPadding(0, systemBars.top, 0, 0);
return insets;
});
+ ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
+ this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
+ drawer.addDrawerListener(toggle);
+ toggle.syncState();
+
+ // Centralized navigation logic
+ navigationView.setNavigationItemSelectedListener(item -> {
+ int id = item.getItemId();
+ if (id == R.id.nav_dashboard) {
+ // Open Data Dashboard
+ startActivity(new Intent(this, activityDataShowing.class));
+ } else if (id == R.id.nav_scout) {
+ // Reset/Restart Scouting flow
+ Intent intent = new Intent(this, ActivityCompetitionSelection.class);
+ intent.putExtra("chooseNewCompetition", true);
+ intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
+ }
+ drawer.closeDrawer(GravityCompat.START);
+ return true;
+ });
+
U u = new U();
RadioGroup competitionRadioGroup = findViewById(R.id.competition_radio_group);
-// RadioButton wpiButton = findViewById(R.id.comp_wpi_button);
-// RadioButton uvmButton = findViewById(R.id.comp_uvm_button);
-// RadioButton dcmpButton = findViewById(R.id.comp_dcmp);
-// RadioButton worldsButton = findViewById(R.id.comp_worlds);
-// RadioButton testButton = findViewById(R.id.comp_test);
Button saveButton = findViewById(R.id.save_button);
Toast unfilledMessage = new Toast(this);
unfilledMessage.setDuration(Toast.LENGTH_SHORT);
@@ -58,6 +94,7 @@ protected void onCreate(Bundle savedInstanceState) {
scoutName = "";
}
+ // Persistence check: If a competition was already selected today, skip this screen
final String FILENAME = "matchAndDate";
File file = new File(this.getFilesDir(), FILENAME);
Calendar now = Calendar.getInstance();
@@ -76,14 +113,15 @@ protected void onCreate(Bundle savedInstanceState) {
dateAndMatchString += reader.readLine();
reader.close();
} catch (FileNotFoundException e) {
- Log.e("File Not Found Exception", e.toString());
+ Log.e("ActivityComp", "Persisted file not found", e);
} catch (IOException e) {
- Log.e("IOException", e.toString());
+ Log.e("ActivityComp", "Read error", e);
}
}
if (!dateAndMatchString.isEmpty()) {
String fileDate = u.untilNextComma(dateAndMatchString);
if (fileDate.equals(currentDate)) {
+ // Fast-forward to scouting if date matches
Intent i = new Intent(this, activityPreMatch.class);
i.putExtra("competition", u.nextCommaOn(dateAndMatchString));
i.putExtra("scoutName", scoutName);
@@ -98,14 +136,15 @@ protected void onCreate(Bundle savedInstanceState) {
if(u.getData(competitionRadioGroup).isEmpty()){
response = "Please choose current competition to be scouting";
}else{
+ // Save selection locally
String fileContents = finalCurrentDate + "," + u.getData(competitionRadioGroup);
try(FileOutputStream fos = this.openFileOutput(FILENAME, Context.MODE_PRIVATE)){
fos.write(fileContents.getBytes());
- } catch(FileNotFoundException e){
- Log.e("File Not Found Exception", e.toString());
} catch(IOException e){
- Log.e("IO Exception", e.toString());
+ Log.e("ActivityComp", "Write error", e);
}
+
+ // Move to Pre-Match
Intent i = new Intent(this, activityPreMatch.class);
i.putExtra("competition", u.getData(competitionRadioGroup));
i.putExtra("scoutName", scoutName);
@@ -117,4 +156,15 @@ protected void onCreate(Bundle savedInstanceState) {
}
});
}
-}
\ No newline at end of file
+
+ @Override
+ public void onBackPressed() {
+ // Handle Sidebar Drawer if open
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ if (drawer != null && drawer.isDrawerOpen(GravityCompat.START)) {
+ drawer.closeDrawer(GravityCompat.START);
+ } else {
+ super.onBackPressed();
+ }
+ }
+}
diff --git a/app/src/main/java/com/example/roboticsscoutingmatchapp/MatchData.java b/app/src/main/java/com/example/roboticsscoutingmatchapp/MatchData.java
new file mode 100644
index 0000000..8dd9027
--- /dev/null
+++ b/app/src/main/java/com/example/roboticsscoutingmatchapp/MatchData.java
@@ -0,0 +1,159 @@
+package com.example.roboticsscoutingmatchapp;
+
+/**
+ * Data model representing a single match scouting report for the 2026 REBUILT game.
+ * Maps directly to the 29-column CSV schema used for data aggregation.
+ */
+public class MatchData {
+ // Basic Match Info
+ public String competition;
+ public int dataVersion; // Incrementing version to track schema changes (e.g., 2026 = v2)
+ public String scout;
+ public String teamNum;
+ public String teamColor;
+ public String matchNum;
+
+ // Pre-Match
+ public boolean preloadedFuel;
+
+ // Autonomous Period
+ public String startingPos;
+ public int autoScored; // Total fuel shots fired in Auto
+ public String autoAccuracy; // Qualitative accuracy percentage (e.g., "75%")
+ public int autoPassed; // Fuel passed to alliance partners
+ public boolean autoHang; // Successful climb to Tower Level 1 during Auto
+
+ // TeleOp Period
+ public int teleopScored; // Total fuel shots fired in TeleOp
+ public String teleopAccuracy;
+ public int teleopPassed;
+ public String hangStatus; // Endgame climb level (Level 1, 2, 3, or None)
+ public int hangTime; // Time remaining when climb was completed
+ public String accuracyPos; // Movement state with best accuracy
+
+ // Field Navigation & Robot State (2026 specific)
+ public boolean overBump;
+ public boolean underTrench;
+ public boolean playedDefense;
+ public boolean collectedFuel;
+ public boolean passedFuel;
+ public boolean didNothing;
+ public boolean other;
+
+ // Post-Match Qualitative Data
+ public String defense; // Description of defense received/given
+ public String stopReason; // Reason if the robot stopped moving
+ public String rank; // Relative rank in the alliance
+ public String comments;
+
+ /**
+ * Constructs a MatchData object from a raw CSV row.
+ * @param csvRow String array containing the 29 values in the schema order.
+ */
+ public MatchData(String[] csvRow) {
+ if (csvRow.length < 29) return;
+
+ this.competition = csvRow[0];
+ this.dataVersion = tryParseInt(csvRow[1]);
+ this.scout = csvRow[2];
+ this.teamNum = csvRow[3];
+ this.teamColor = csvRow[4];
+ this.matchNum = csvRow[5];
+ this.preloadedFuel = Boolean.parseBoolean(csvRow[6]);
+ this.startingPos = csvRow[7];
+ this.autoScored = tryParseInt(csvRow[8]);
+ this.autoAccuracy = csvRow[9];
+ this.autoPassed = tryParseInt(csvRow[10]);
+ this.autoHang = Boolean.parseBoolean(csvRow[11]);
+ this.teleopScored = tryParseInt(csvRow[12]);
+ this.teleopAccuracy = csvRow[13];
+ this.teleopPassed = tryParseInt(csvRow[14]);
+ this.hangStatus = csvRow[15];
+ this.hangTime = tryParseInt(csvRow[16]);
+ this.accuracyPos = csvRow[17];
+ this.overBump = Boolean.parseBoolean(csvRow[18]);
+ this.underTrench = Boolean.parseBoolean(csvRow[19]);
+ this.playedDefense = Boolean.parseBoolean(csvRow[20]);
+ this.collectedFuel = Boolean.parseBoolean(csvRow[21]);
+ this.passedFuel = Boolean.parseBoolean(csvRow[22]);
+ this.didNothing = Boolean.parseBoolean(csvRow[23]);
+ this.other = Boolean.parseBoolean(csvRow[24]);
+ this.defense = csvRow[25];
+ this.stopReason = csvRow[26];
+ this.rank = csvRow[27];
+ this.comments = csvRow[28];
+ }
+
+ /**
+ * Estimates the number of fuel pieces scored in Auto.
+ * Calculation: Total Shots * Accuracy Multiplier
+ */
+ public int getCalculatedAutoScoredWhole() {
+ return (int) Math.round(autoScored * getAccuracyMultiplier(autoAccuracy));
+ }
+
+ /**
+ * Estimates the number of fuel pieces scored in TeleOp.
+ */
+ public int getCalculatedTeleopScoredWhole() {
+ return (int) Math.round(teleopScored * getAccuracyMultiplier(teleopAccuracy));
+ }
+
+ /**
+ * Calculates total points earned in Autonomous.
+ * Includes fuel points and the 15pt Auto Climb bonus.
+ */
+ public double getCalculatedAutoScore() {
+ return (double) getCalculatedAutoScoredWhole() + (autoHang ? 15 : 0);
+ }
+
+ /**
+ * Calculates total points earned in TeleOp/Endgame.
+ * Includes fuel points and Tower climb points (10, 20, or 30).
+ */
+ public double getCalculatedTeleopScore() {
+ return (double) getCalculatedTeleopScoredWhole() + getClimbPoints();
+ }
+
+ /**
+ * Total match score contribution by this robot.
+ */
+ public double getTotalScore() {
+ return getCalculatedAutoScore() + getCalculatedTeleopScore();
+ }
+
+ /**
+ * Maps the hangStatus string to specific point values for the 2026 REBUILT game.
+ */
+ public int getClimbPoints() {
+ if (hangStatus == null) return 0;
+ switch (hangStatus) {
+ case "Level 1": return 10;
+ case "Level 2": return 20;
+ case "Level 3": return 30;
+ default: return 0;
+ }
+ }
+
+ /**
+ * Converts qualitative accuracy strings to numeric multipliers.
+ * Note: "More than 95%" is treated as 0.97 to account for occasional misses.
+ */
+ private double getAccuracyMultiplier(String accuracy) {
+ if (accuracy == null) return 0;
+ if (accuracy.contains("10%")) return 0.05;
+ if (accuracy.contains("25%")) return 0.25;
+ if (accuracy.contains("50%")) return 0.50;
+ if (accuracy.contains("75%")) return 0.75;
+ if (accuracy.contains("95%")) return 0.97;
+ return 0;
+ }
+
+ private int tryParseInt(String val) {
+ try {
+ return Integer.parseInt(val.trim());
+ } catch (Exception e) {
+ return 0;
+ }
+ }
+}
diff --git a/app/src/main/java/com/example/roboticsscoutingmatchapp/MatchHistoryAdapter.java b/app/src/main/java/com/example/roboticsscoutingmatchapp/MatchHistoryAdapter.java
new file mode 100644
index 0000000..e9bad4e
--- /dev/null
+++ b/app/src/main/java/com/example/roboticsscoutingmatchapp/MatchHistoryAdapter.java
@@ -0,0 +1,93 @@
+package com.example.roboticsscoutingmatchapp;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import java.util.List;
+
+/**
+ * Adapter for displaying a list of MatchData objects in a RecyclerView.
+ * Used in the Data Hub Dashboard to show match history for a specific team.
+ */
+public class MatchHistoryAdapter extends RecyclerView.Adapter {
+
+ /**
+ * Listener interface for match selection events.
+ */
+ public interface OnMatchClickListener {
+ void onMatchClick(MatchData match);
+ }
+
+ private List matches;
+ private OnMatchClickListener listener;
+
+ public MatchHistoryAdapter(List matches, OnMatchClickListener listener) {
+ this.matches = matches;
+ this.listener = listener;
+ }
+
+ /**
+ * Updates the dataset and refreshes the UI list.
+ */
+ public void updateData(List newMatches) {
+ this.matches = newMatches;
+ notifyDataSetChanged();
+ }
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.item_match_history, parent, false);
+ return new ViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+ MatchData match = matches.get(position);
+
+ // Display summary info for the match row
+ holder.matchNum.setText("M #" + match.matchNum);
+
+ // Use calculated pieces scored (Shots * Accuracy) for the summary text
+ holder.scoreSummary.setText(String.format("Auto: %d | Tele: %d",
+ match.getCalculatedAutoScoredWhole(),
+ match.getCalculatedTeleopScoredWhole()));
+
+ holder.hangStatus.setText("Climb: " + match.hangStatus);
+ holder.scouterName.setText(match.scout);
+
+ // Click listener to open the detailed match popup
+ holder.itemView.setOnClickListener(v -> {
+ if (listener != null) {
+ listener.onMatchClick(match);
+ }
+ });
+ }
+
+
+ @Override
+ public int getItemCount() {
+ return matches.size();
+ }
+
+ /**
+ * ViewHolder for the match history list items.
+ */
+ public static class ViewHolder extends RecyclerView.ViewHolder {
+ TextView matchNum, scoreSummary, hangStatus, scouterName;
+
+ public ViewHolder(@NonNull View itemView) {
+ super(itemView);
+ matchNum = itemView.findViewById(R.id.match_num_text);
+ scoreSummary = itemView.findViewById(R.id.score_summary_text);
+ hangStatus = itemView.findViewById(R.id.hang_status_text);
+ scouterName = itemView.findViewById(R.id.scout_name_text);
+ }
+ }
+}
diff --git a/app/src/main/java/com/example/roboticsscoutingmatchapp/U.java b/app/src/main/java/com/example/roboticsscoutingmatchapp/U.java
index 5cfb3e1..b52fc04 100644
--- a/app/src/main/java/com/example/roboticsscoutingmatchapp/U.java
+++ b/app/src/main/java/com/example/roboticsscoutingmatchapp/U.java
@@ -21,7 +21,7 @@
public class U extends AppCompatActivity{
- public final int DATA_VERSION = 1;
+ public final int DATA_VERSION = 2;
// Characters to strip in input fields for stripText method
public static final String [] DELIMITER = {",", ";", ":", "|", "\n"};
public static final String [] WHITESPACE = {" "};
diff --git a/app/src/main/java/com/example/roboticsscoutingmatchapp/activityAfterMatch.java b/app/src/main/java/com/example/roboticsscoutingmatchapp/activityAfterMatch.java
index 2f1d30f..bdaec47 100644
--- a/app/src/main/java/com/example/roboticsscoutingmatchapp/activityAfterMatch.java
+++ b/app/src/main/java/com/example/roboticsscoutingmatchapp/activityAfterMatch.java
@@ -22,23 +22,26 @@
import java.io.FileWriter;
import java.io.IOException;
+/**
+ * Activity for collecting Post-Match qualitative data (Field navigation, robot state, stop reason).
+ * Finalizes and saves the scouting report as a CSV file.
+ */
public class activityAfterMatch extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState); // Default code start
+ super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_after_match);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
- }); // Default code end
-
- U u = new U(); // defines new utilities object to be used
+ });
- // *---Defines all the components on the current page as variables---*
+ U u = new U();
+ // 2026 REBUILT Metric Collection
CheckBox overBump = findViewById(R.id.over_bump);
CheckBox underTrench = findViewById(R.id.under_trench);
CheckBox playedDefense = findViewById(R.id.played_defense);
@@ -67,28 +70,30 @@ protected void onCreate(Bundle savedInstanceState) {
EditText finalText = findViewById(R.id.End_of_app_text);
Button saveButton = findViewById(R.id.save_button);
Button backButton = findViewById(R.id.back_button);
- Toast unfilledMessage = new Toast(this); // Creates a Toast (pop-up message object)
- unfilledMessage.setDuration(Toast.LENGTH_SHORT); // Sets it to only be angry a short while
+
+ Toast unfilledMessage = new Toast(this);
+ unfilledMessage.setDuration(Toast.LENGTH_SHORT);
- String preMatchSaveString, autoSaveString, // Define savestring String objects
+ // Load intent data for UI state persistence
+ String preMatchSaveString, autoSaveString,
teleOpSaveString, postMatchSaveString;
- Bundle extras = getIntent().getExtras(); // Gets the savestrings and stores them to extras
- if(extras != null){ // If savestrings exist, set individual strings to values
+ Bundle extras = getIntent().getExtras();
+ if(extras != null){
preMatchSaveString = extras.getString("preMatch", "");
autoSaveString = extras.getString("auto", "");
teleOpSaveString = extras.getString("teleOp", "");
postMatchSaveString = extras.getString("postMatch", "");
- } else { // If savestrings don't exist, no value
+ } else {
preMatchSaveString = "";
autoSaveString = "";
teleOpSaveString = "";
postMatchSaveString = "";
}
- if(!postMatchSaveString.isEmpty()){ // Sets all the components to the values within the savestring
- overBump.setChecked(Boolean.parseBoolean(u.untilNextComma(postMatchSaveString))); // Sets the value to the parsed value in the savestring
- postMatchSaveString = u.nextCommaOn(postMatchSaveString); // removes the value from the savestring
- // So on an so forth
+ // Parse backward-passed post-match data strings to restore UI state
+ if(!postMatchSaveString.isEmpty()){
+ overBump.setChecked(Boolean.parseBoolean(u.untilNextComma(postMatchSaveString)));
+ postMatchSaveString = u.nextCommaOn(postMatchSaveString);
underTrench.setChecked(Boolean.parseBoolean(u.untilNextComma(postMatchSaveString)));
postMatchSaveString = u.nextCommaOn(postMatchSaveString);
playedDefense.setChecked(Boolean.parseBoolean(u.untilNextComma(postMatchSaveString)));
@@ -113,35 +118,19 @@ protected void onCreate(Bundle savedInstanceState) {
String stopReasonString = u.untilNextComma(postMatchSaveString);
switch (stopReasonString) {
- case "Died":
- diedButton.toggle();
- break;
- case "Tipped":
- tippedButton.toggle();
- break;
- case "Physically Broke":
- physicallyBrokeButton.toggle();
- break;
- case "E-stopped":
- eStoppedButton.toggle();
- break;
- case "Not Stopped":
- notStoppedButton.toggle();
- break;
+ case "Died": diedButton.toggle(); break;
+ case "Tipped": tippedButton.toggle(); break;
+ case "Physically Broke": physicallyBrokeButton.toggle(); break;
+ case "E-stopped": eStoppedButton.toggle(); break;
+ case "Not Stopped": notStoppedButton.toggle(); break;
}
postMatchSaveString = u.nextCommaOn(postMatchSaveString);
String teamRank = u.untilNextComma(postMatchSaveString);
switch(teamRank){
- case "Rank 1":
- rank1Button.toggle();
- break;
- case "Rank 2":
- rank2Button.toggle();
- break;
- case "Rank 3":
- rank3Button.toggle();
- break;
+ case "Rank 1": rank1Button.toggle(); break;
+ case "Rank 2": rank2Button.toggle(); break;
+ case "Rank 3": rank3Button.toggle(); break;
}
postMatchSaveString = u.nextCommaOn(postMatchSaveString);
@@ -149,12 +138,11 @@ protected void onCreate(Bundle savedInstanceState) {
postMatchSaveString = u.nextCommaOn(postMatchSaveString);
}
- backButton.setOnClickListener((l)->{ // Sets current savestring to current values of components
- // Because it's the back button, these values can have no value
+ backButton.setOnClickListener((l)->{
String afterMatchInfo = "";
-
- afterMatchInfo += u.getData(underTrench) + ",";
+ // Maintain data integrity for the 29-column schema even on back-nav
afterMatchInfo += u.getData(overBump) + ",";
+ afterMatchInfo += u.getData(underTrench) + ",";
afterMatchInfo += u.getData(playedDefense) + ",";
afterMatchInfo += u.getData(collectedFuel) + ",";
afterMatchInfo += u.getData(passedFuel) + ",";
@@ -170,20 +158,18 @@ protected void onCreate(Bundle savedInstanceState) {
i.putExtra("auto", autoSaveString);
i.putExtra("teleOp", teleOpSaveString);
i.putExtra("postMatch", afterMatchInfo);
-
this.startActivity(i);
});
- saveButton.setOnClickListener((l)->{ // Sets current savestring to current component values
- // Checks that components that need to be filled in are filled in
+ saveButton.setOnClickListener((l)->{
String response = "";
- if(u.getData(defenseReceivedGroup).isEmpty()) // If a component is not filled in
- response = "Please fill in defense received"; // Sets the toast message to error message
+ if(u.getData(defenseReceivedGroup).isEmpty())
+ response = "Please fill in defense received";
else if(u.getData(rankGroup).isEmpty())
response = "Please fill in rank";
else if(u.getData(stopReasonGroup).isEmpty())
response = "Please fill in stop reason";
- else{ // If nothing is wrong, keep filling everything in
+ else{
String postMatchInfo = "";
postMatchInfo += u.getData(overBump) + ",";
postMatchInfo += u.getData(underTrench) + ",";
@@ -197,44 +183,35 @@ else if(u.getData(stopReasonGroup).isEmpty())
postMatchInfo += u.getData(rankGroup) + ",";
postMatchInfo += u.stripText(u.getData(finalText), u.DELIMITER) + ",";
- // Competition Location | Save Version | Scout Name | Team # | Team Color |
- // Match Number | Preloaded coral ||
- // Filename format: "match_scouting_" + [team_number] + [match number]
+ // Compile final CSV string and write to external storage (Documents directory)
+ // Filename format: match_scouting_[comp]_[team]_[match].csv
String teamNumber = u.untilNextComma(u.nextCommaOn(u.nextCommaOn(u.nextCommaOn(preMatchSaveString))));
String matchNumber = u.untilNextComma(u.nextCommaOn(u.nextCommaOn(u.nextCommaOn(u.nextCommaOn(u.nextCommaOn(preMatchSaveString))))));
String competitionLocation = u.untilNextComma(preMatchSaveString);
String scoutName = u.untilNextComma(u.nextCommaOn(u.nextCommaOn(preMatchSaveString)));
String fileName = "match_scouting_"+ competitionLocation + "_" + teamNumber + "_" + matchNumber + ".csv";
- Log.d("File Name: ", fileName);
try {
File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), fileName);
if (!file.exists()) {
- boolean fileCreated = file.createNewFile();
- if(!fileCreated) {
- Toast.makeText(this, "File " + fileName + " has not been created", Toast.LENGTH_SHORT).show();
- Log.d("File written: ", "File not created");
- }else {
- Toast.makeText(this, "File " + fileName + " has been created", Toast.LENGTH_SHORT).show();
- Log.d("File written: ", "File created");
- }
- }else{
- Log.d("File already existed", "File already existed");
+ file.createNewFile();
}
FileWriter fw = new FileWriter(file.getAbsoluteFile());
BufferedWriter bw = new BufferedWriter(fw);
-
+
+ // Final Write: Pre + Auto + Tele + Post = 29 Columns
bw.write(preMatchSaveString);
bw.write(autoSaveString);
bw.write(teleOpSaveString);
bw.write(postMatchInfo);
bw.flush();
bw.close();
+ Toast.makeText(this, "File " + fileName + " Saved", Toast.LENGTH_SHORT).show();
} catch (IOException e) {
- Log.d("Error thrown:", "+==========+Error Thrown+==========+");
- Log.getStackTraceString(e);
+ Log.e("activityAfterMatch", "File save failed", e);
}
+
Intent i = new Intent(this, ActivityCompetitionSelection.class);
i.putExtra("scoutName", scoutName);
this.startActivity(i);
@@ -246,4 +223,4 @@ else if(u.getData(stopReasonGroup).isEmpty())
}
});
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/com/example/roboticsscoutingmatchapp/activityAutonomous.java b/app/src/main/java/com/example/roboticsscoutingmatchapp/activityAutonomous.java
index 2578bd6..df7dc25 100644
--- a/app/src/main/java/com/example/roboticsscoutingmatchapp/activityAutonomous.java
+++ b/app/src/main/java/com/example/roboticsscoutingmatchapp/activityAutonomous.java
@@ -20,8 +20,16 @@
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
+/**
+ * Activity for collecting Autonomous period data.
+ * Tracks starting position, fuel shots fired, accuracy, and auto climb.
+ */
public class activityAutonomous extends AppCompatActivity {
+ /**
+ * Helper to clear a RadioGroup when a button in another group is selected.
+ * Used for multi-column starting position layouts.
+ */
public void clearGroup(RadioGroup field1, RadioGroup field2){
field1.setOnCheckedChangeListener(null);
field1.check(-1);
@@ -31,9 +39,9 @@ public void clearGroup(RadioGroup field1, RadioGroup field2){
@Override
protected void onCreate(Bundle savedInstanceState) {
-
U u = new U();
+ // Load existing match state
String preMatchSaveString, autoSaveString,
teleOpSaveString, postMatchSaveString;
Bundle extras = getIntent().getExtras();
@@ -59,11 +67,13 @@ protected void onCreate(Bundle savedInstanceState) {
return insets;
});
+ // UI Binding - Starting Position
RadioGroup positionGroup1 = findViewById(R.id.staring_position_radio_group1);
RadioButton position1Button = findViewById(R.id.Position_1);
RadioButton position2Button = findViewById(R.id.Position_2);
RadioButton position3Button = findViewById(R.id.Position_3);
+ // UI Binding - Fuel Shot Counters (FS)
Button FS1plus = findViewById(R.id.up_count_button_fs1);
Button FS5plus = findViewById(R.id.up_count_button_fs5);
Button FS10plus = findViewById(R.id.up_count_button_fs10);
@@ -76,6 +86,7 @@ protected void onCreate(Bundle savedInstanceState) {
Button FS15minus = findViewById(R.id.down_count_button_fs15);
Button FS20minus = findViewById(R.id.down_count_button_fs20);
+ // UI Binding - Fuel Passing Counters (FP)
Button FP1plus = findViewById(R.id.up_count_button_fp1);
Button FP5plus = findViewById(R.id.up_count_button_fp5);
Button FP10plus = findViewById(R.id.up_count_button_fp10);
@@ -91,6 +102,7 @@ protected void onCreate(Bundle savedInstanceState) {
EditText FSField = findViewById(R.id.edit_text_fs);
EditText FPField = findViewById(R.id.edit_text_fp);
+ // UI Binding - Accuracy & Performance
RadioGroup accuracyGroup = findViewById(R.id.accuracy_radio_group);
RadioButton lessThanTen = findViewById(R.id.underTen);
RadioButton twentyFivePercent = findViewById(R.id.twentyFivePercent);
@@ -98,49 +110,32 @@ protected void onCreate(Bundle savedInstanceState) {
RadioButton seventyFivePercent = findViewById(R.id.seventyFivePercent);
RadioButton overNinetyFive = findViewById(R.id.overNinetyFive);
+ CheckBox autoHang = findViewById(R.id.checkBox_auto_hang);
+
Button backButton = findViewById(R.id.back_button);
Button saveButton = findViewById(R.id.save_button);
- CheckBox autoHang = findViewById(R.id.checkBox_auto_hang);
-
Toast unfilledMessage = new Toast(this);
unfilledMessage.setDuration(Toast.LENGTH_SHORT);
+ // Restore UI state from saved data string
if(!autoSaveString.isEmpty()){
- // Starting Position | #Shots Fired | Accuracy % | #Fuel Passed | Auto Hang |
String position = u.untilNextComma(autoSaveString);
-// Log.d(position, position);
switch (position){
- case "Position 1":
- position1Button.toggle();
- break;
- case "Position 2":
- position2Button.toggle();
- break;
- case "Position 3":
- position3Button.toggle();
- break;
+ case "Position 1": position1Button.toggle(); break;
+ case "Position 2": position2Button.toggle(); break;
+ case "Position 3": position3Button.toggle(); break;
}
autoSaveString = u.nextCommaOn(autoSaveString);
String accuracyChoice = u.untilNextComma(autoSaveString);
switch(accuracyChoice){
- case "Less Than 10%":
- lessThanTen.toggle();
- break;
- case "25%":
- twentyFivePercent.toggle();
- break;
- case "50%":
- fiftyPercent.toggle();
- break;
- case "75%":
- seventyFivePercent.toggle();
- break;
- case "More Than 95%":
- overNinetyFive.toggle();
- break;
+ case "Less Than 10%": lessThanTen.toggle(); break;
+ case "25%": twentyFivePercent.toggle(); break;
+ case "50%": fiftyPercent.toggle(); break;
+ case "75%": seventyFivePercent.toggle(); break;
+ case "More Than 95%": overNinetyFive.toggle(); break;
}
autoSaveString = u.nextCommaOn(autoSaveString);
@@ -148,6 +143,7 @@ protected void onCreate(Bundle savedInstanceState) {
autoSaveString = u.nextCommaOn(autoSaveString);
}
+ // Setup Increment/Decrement Listeners
FS1plus.setOnClickListener((l)->u.incrementText(FSField));
FS1minus.setOnClickListener((l)->u.incrementText(FSField, -1));
FS5plus.setOnClickListener((l)->u.incrementText(FSField, +5));
@@ -169,19 +165,15 @@ protected void onCreate(Bundle savedInstanceState) {
FP15minus.setOnClickListener((l)->u.incrementText(FPField, -15));
FP20plus.setOnClickListener((l)->u.incrementText(FPField, +20));
FP20minus.setOnClickListener((l)->u.incrementText(FPField, -20));
- // Sets all the buttons to either increment or decrement their respective buttons.
backButton.setOnClickListener((l)-> {
- // Starting Position | #Shots Fired | Accuracy % | #Fuel Passed | Auto Hang |
String autoInfo = "";
autoInfo += u.getData(positionGroup1);
-
autoInfo += u.getData(FSField);
autoInfo += u.getData(accuracyGroup);
-
autoInfo += u.getData(FPField);
-
autoInfo += u.getData(autoHang);
+
Intent i = new Intent(this, activityPreMatch.class);
i.putExtra("preMatch", preMatchSaveString);
i.putExtra("auto", autoInfo);
@@ -194,22 +186,17 @@ protected void onCreate(Bundle savedInstanceState) {
saveButton.setOnClickListener((l)-> {
String response = "";
-
if((u.getData(positionGroup1).isEmpty()))
response = "Please fill position";
if ((u.getData(accuracyGroup).isEmpty()))
response = "Please give accuracy";
else{
-
+ // Serialize auto data
String autoInfo = "";
-
autoInfo += u.getData(positionGroup1) + ",";
-
autoInfo += u.getData(FSField) + ",";
autoInfo += u.getData(accuracyGroup) + ",";
-
autoInfo += u.getData(FPField) + ",";
-
autoInfo += u.getData(autoHang) + ",";
Intent i = new Intent(this, activityTeleOp.class);
@@ -227,4 +214,4 @@ protected void onCreate(Bundle savedInstanceState) {
}
});
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/com/example/roboticsscoutingmatchapp/activityDataShowing.java b/app/src/main/java/com/example/roboticsscoutingmatchapp/activityDataShowing.java
index dd87fd3..ba645a2 100644
--- a/app/src/main/java/com/example/roboticsscoutingmatchapp/activityDataShowing.java
+++ b/app/src/main/java/com/example/roboticsscoutingmatchapp/activityDataShowing.java
@@ -1,69 +1,474 @@
package com.example.roboticsscoutingmatchapp;
+import android.content.Intent;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.AutoCompleteTextView;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.Spinner;
+import android.widget.TextView;
import androidx.activity.EdgeToEdge;
+import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+import androidx.core.content.ContextCompat;
import androidx.core.graphics.Insets;
+import androidx.core.view.GravityCompat;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
+import androidx.drawerlayout.widget.DrawerLayout;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.github.mikephil.charting.charts.HorizontalBarChart;
+import com.github.mikephil.charting.charts.LineChart;
+import com.github.mikephil.charting.components.XAxis;
+import com.github.mikephil.charting.data.BarData;
+import com.github.mikephil.charting.data.BarDataSet;
+import com.github.mikephil.charting.data.BarEntry;
+import com.github.mikephil.charting.data.Entry;
+import com.github.mikephil.charting.data.LineData;
+import com.github.mikephil.charting.data.LineDataSet;
+import com.github.mikephil.charting.formatter.IndexAxisValueFormatter;
+import com.google.android.material.dialog.MaterialAlertDialogBuilder;
+import com.google.android.material.navigation.NavigationView;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+/**
+ * Activity for the Data Hub Dashboard.
+ * Aggregates local CSV scouting data to display team trends, averages, and a leaderboard.
+ */
public class activityDataShowing extends AppCompatActivity {
+ private final List allMatches = new ArrayList<>();
+ private final Set teamNumbers = new HashSet<>();
+ private final Set competitions = new HashSet<>();
+
+ private MatchHistoryAdapter adapter;
+ private TextView avgAutoText, avgTeleopText, avgPassingText, climbRateText;
+ private LineChart scoringChart;
+ private HorizontalBarChart leaderboardChart;
+ private Spinner compFilterSpinner, metricSpinner;
+ private TextView leaderboardPageText;
+
+ private int leaderboardPage = 0;
+ private static final int PAGE_SIZE = 5;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_data_showing);
- ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
+
+ // UI Initialization
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ NavigationView navigationView = findViewById(R.id.nav_view);
+
+ // Fix status bar overlap for the AppBar and the Navigation Drawer
+ ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.app_bar), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
- v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
+ v.setPadding(0, systemBars.top, 0, 0);
return insets;
});
- final String BASE = "match_scouting_2025";
- String base = BASE + ".csv";
- String comp = BASE + "_comp.csv";
- String radar = BASE + "_radar.csv";
- String aggregated = BASE + "_agg.csv";
- String delimiter = ",";
-
- ArrayList aggArr = new ArrayList<>();
-
-
- boolean baseE = false;
- boolean compE = false;
- boolean radarE = false;
- boolean aggE = false;
-
- try{
- File baseF = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), base);
- File compF = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), comp);
- File radarF = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), radar);
- File aggF = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), aggregated);
-
- if(baseF.exists()){baseE = true;}
- if(compF.exists()){compE = true;}
- if(radarF.exists()){radarE = true;}
- if(aggF.exists()){aggE = true;}
-
- if(aggE) {
- BufferedReader brAgg = new BufferedReader(new FileReader(aggF));
- String line;
- while((line = brAgg.readLine()) != null){
- aggArr.add(line.split(delimiter));
+ // Setup Sidebar Toggle
+ ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
+ this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
+ drawer.addDrawerListener(toggle);
+ toggle.syncState();
+
+ // Handle Sidebar Navigation
+ navigationView.setNavigationItemSelectedListener(item -> {
+ int id = item.getItemId();
+ if (id == R.id.nav_scout) {
+ Intent intent = new Intent(this, ActivityCompetitionSelection.class);
+ intent.putExtra("chooseNewCompetition", true);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
+ }
+ drawer.closeDrawer(GravityCompat.START);
+ return true;
+ });
+
+ // Bind UI Components
+ avgAutoText = findViewById(R.id.avg_auto_scored);
+ avgTeleopText = findViewById(R.id.avg_teleop_scored);
+ avgPassingText = findViewById(R.id.avg_passing);
+ climbRateText = findViewById(R.id.climb_success_rate);
+ scoringChart = findViewById(R.id.scoring_line_chart);
+ leaderboardChart = findViewById(R.id.leaderboard_bar_chart);
+ compFilterSpinner = findViewById(R.id.comp_filter_spinner);
+ metricSpinner = findViewById(R.id.leaderboard_metric_spinner);
+ leaderboardPageText = findViewById(R.id.leaderboard_page_text);
+
+ // Leaderboard Pagination
+ Button prevButton = findViewById(R.id.prev_leaderboard_button);
+ Button nextButton = findViewById(R.id.next_leaderboard_button);
+ prevButton.setOnClickListener(v -> {
+ if (leaderboardPage > 0) {
+ leaderboardPage--;
+ refreshLeaderboard();
+ }
+ });
+ nextButton.setOnClickListener(v -> {
+ leaderboardPage++;
+ refreshLeaderboard();
+ });
+
+ // Data Lifecycle: Load -> Setup UI -> Filter
+ setupCharts();
+ loadLocalData();
+ setupDashboard();
+ setupCompSpinner();
+ setupMetricSpinner();
+ }
+
+ private void setupCharts() {
+ // Configure visual styles for MPAndroidChart components
+ scoringChart.getDescription().setEnabled(false);
+ scoringChart.setDrawGridBackground(false);
+ scoringChart.getAxisRight().setEnabled(false);
+ scoringChart.getXAxis().setPosition(XAxis.XAxisPosition.BOTTOM);
+ scoringChart.getLegend().setEnabled(false);
+ scoringChart.setNoDataText("Select a team to view scoring trend");
+
+ leaderboardChart.getDescription().setEnabled(false);
+ leaderboardChart.setDrawGridBackground(false);
+ leaderboardChart.getAxisRight().setEnabled(false);
+ leaderboardChart.getAxisLeft().setAxisMinimum(0f);
+
+ XAxis leaderX = leaderboardChart.getXAxis();
+ leaderX.setPosition(XAxis.XAxisPosition.BOTTOM);
+ leaderX.setDrawGridLines(false);
+ leaderX.setGranularity(1f);
+ leaderX.setLabelCount(5);
+
+ leaderboardChart.setExtraOffsets(0, 0, 40, 0);
+ leaderboardChart.getLegend().setEnabled(false);
+ leaderboardChart.setFitBars(true);
+ }
+
+ /**
+ * Scans the public Documents directory for match_scouting_*.csv files.
+ * Parses each file into a MatchData object for aggregation.
+ */
+ private void loadLocalData() {
+ File docDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS);
+ File[] files = docDir.listFiles((dir, name) -> name.startsWith("match_scouting_") && name.endsWith(".csv"));
+
+ if (files == null) return;
+
+ for (File file : files) {
+ try (BufferedReader br = new BufferedReader(new FileReader(file))) {
+ String line = br.readLine();
+ if (line != null) {
+ // split with -1 to preserve empty trailing fields (like comments)
+ String[] data = line.split(",", -1);
+ MatchData match = new MatchData(data);
+ if (match.teamNum != null && !match.teamNum.isEmpty()) {
+ allMatches.add(match);
+ teamNumbers.add(match.teamNum);
+ if (match.competition != null && !match.competition.isEmpty()) {
+ competitions.add(match.competition);
+ }
+ }
}
+ } catch (Exception e) {
+ Log.e("DataShowing", "Error loading file: " + file.getName(), e);
}
- } catch (Exception e) {
- Log.e("e", "onCreate: ", e);
}
+ }
+
+ private void setupDashboard() {
+ // Setup Search/Autocomplete for team selection
+ AutoCompleteTextView searchView = findViewById(R.id.team_search_view);
+ ArrayAdapter teamAdapter = new ArrayAdapter<>(this,
+ android.R.layout.simple_dropdown_item_1line, new ArrayList<>(teamNumbers));
+ searchView.setAdapter(teamAdapter);
+
+ // Setup Match History List
+ RecyclerView recyclerView = findViewById(R.id.match_history_recycler);
+ recyclerView.setLayoutManager(new LinearLayoutManager(this));
+ adapter = new MatchHistoryAdapter(new ArrayList<>(), this::showMatchDetails);
+ recyclerView.setAdapter(adapter);
+
+ searchView.setOnItemClickListener((parent, view, position, id) -> {
+ String selectedTeam = (String) parent.getItemAtPosition(position);
+ updateDashboard(selectedTeam);
+ });
+ }
+
+ /**
+ * Displays a detailed breakdown of a single match in an AlertDialog.
+ */
+ private void showMatchDetails(MatchData match) {
+ StringBuilder details = new StringBuilder();
+ details.append("Scouter: ").append(match.scout).append("\n");
+ details.append("Competition: ").append(match.competition).append("\n");
+ details.append("Team Color: ").append(match.teamColor).append("\n\n");
+
+ details.append("--- Autonomous ---\n");
+ details.append("Fuel Scored: ").append(match.autoScored).append("\n");
+ details.append("Fuel Passed: ").append(match.autoPassed).append("\n");
+ details.append("Accuracy: ").append(match.autoAccuracy).append("\n");
+ details.append("Auto Climb (L1): ").append(match.autoHang ? "Yes (+15 pts)" : "No").append("\n\n");
+
+ details.append("--- TeleOp ---\n");
+ details.append("Fuel Scored: ").append(match.teleopScored).append("\n");
+ details.append("Fuel Passed: ").append(match.teleopPassed).append("\n");
+ details.append("Accuracy: ").append(match.teleopAccuracy).append("\n");
+ details.append("Climb Level: ").append(match.hangStatus).append(" (+").append(match.getClimbPoints()).append(" pts)\n");
+ details.append("Climb Time: ").append(match.hangTime).append("s\n\n");
+
+ details.append("--- Performance & Notes ---\n");
+ details.append("Accuracy Pos: ").append(match.accuracyPos).append("\n");
+ details.append("Played Defense: ").append(match.playedDefense ? "Yes" : "No").append("\n");
+ details.append("Defense Notes: ").append(match.defense).append("\n");
+ details.append("Stop Reason: ").append(match.stopReason).append("\n");
+ details.append("Notes: ").append(match.comments);
+
+ new MaterialAlertDialogBuilder(this)
+ .setTitle("Match #" + match.matchNum + " Details")
+ .setMessage(details.toString())
+ .setPositiveButton("Close", null)
+ .show();
+ }
+
+ private void setupCompSpinner() {
+ List compList = new ArrayList<>();
+ compList.add("All Competitions");
+ compList.addAll(competitions);
+
+ ArrayAdapter spinnerAdapter = new ArrayAdapter<>(this,
+ android.R.layout.simple_spinner_item, compList);
+ spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ compFilterSpinner.setAdapter(spinnerAdapter);
+
+ compFilterSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ leaderboardPage = 0;
+ refreshLeaderboard();
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {}
+ });
+ }
+
+ private void setupMetricSpinner() {
+ String[] metrics = {"Total Score", "Auto Fuel", "Tele Fuel", "Passing", "Accuracy %"};
+ ArrayAdapter metricAdapter = new ArrayAdapter<>(this,
+ android.R.layout.simple_spinner_item, metrics);
+ metricAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ metricSpinner.setAdapter(metricAdapter);
+
+ metricSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ leaderboardPage = 0;
+ refreshLeaderboard();
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {}
+ });
+ }
+ private void refreshLeaderboard() {
+ String comp = (String) compFilterSpinner.getSelectedItem();
+ String metric = (String) metricSpinner.getSelectedItem();
+ displayTopTeams(comp.equals("All Competitions") ? null : comp, metric);
+ }
+
+ /**
+ * Calculates and displays the top 5 teams based on the selected metric and competition filter.
+ */
+ private void displayTopTeams(String competitionFilter, String metric) {
+ Map> teamValues = new HashMap<>();
+ for (MatchData m : allMatches) {
+ if (competitionFilter == null || m.competition.equals(competitionFilter)) {
+ double val = 0;
+ switch (metric) {
+ case "Total Score": val = m.getTotalScore(); break;
+ case "Auto Fuel": val = m.autoScored; break;
+ case "Tele Fuel": val = m.teleopScored; break;
+ case "Passing": val = (m.autoPassed + m.teleopPassed); break;
+ case "Accuracy %":
+ // Simplified accuracy metric for leaderboard
+ val = m.getCalculatedTeleopScoredWhole() / Math.max(1.0, m.teleopScored) * 100;
+ break;
+ }
+ teamValues.computeIfAbsent(m.teamNum, k -> new ArrayList<>()).add(val);
+ }
+ }
+
+ // Aggregate by Team Number and Sort Descending
+ List> sortedValues = teamValues.entrySet().stream()
+ .map(entry -> {
+ double avg = entry.getValue().stream().mapToDouble(d -> d).average().orElse(0.0);
+ return new HashMap.SimpleEntry<>(entry.getKey(), avg);
+ })
+ .sorted((e1, e2) -> Double.compare(e2.getValue(), e1.getValue()))
+ .collect(Collectors.toList());
+
+ // Handle Pagination
+ int start = leaderboardPage * PAGE_SIZE;
+ if (start >= sortedValues.size()) {
+ if (leaderboardPage > 0) {
+ leaderboardPage--;
+ return;
+ }
+ leaderboardChart.clear();
+ leaderboardChart.setNoDataText("No data for selection");
+ leaderboardPageText.setText("No Data");
+ return;
+ }
+
+ int end = Math.min(start + PAGE_SIZE, sortedValues.size());
+ List> pageValues = sortedValues.subList(start, end);
+
+ leaderboardPageText.setText(String.format(Locale.getDefault(), "Rank %d-%d", start + 1, end));
+ updateLeaderboardChart(pageValues, metric, start);
+ }
+
+ private void updateLeaderboardChart(List> data, String metricLabel, int startRank) {
+ List entries = new ArrayList<>();
+ List teamLabels = new ArrayList<>();
+
+ // Reverse for Horizontal Chart display (highest at top)
+ List> displayData = new ArrayList<>(data);
+ Collections.reverse(displayData);
+
+ for (int i = 0; i < displayData.size(); i++) {
+ Map.Entry entry = displayData.get(i);
+ entries.add(new BarEntry(i, entry.getValue().floatValue()));
+ int rank = startRank + displayData.size() - i;
+ teamLabels.add("#" + rank + " Team " + entry.getKey());
+ }
+
+ BarDataSet dataSet = new BarDataSet(entries, metricLabel);
+ dataSet.setColor(ContextCompat.getColor(this, R.color.aztech_blue));
+ dataSet.setValueTextSize(10f);
+
+ BarData barData = new BarData(dataSet);
+ leaderboardChart.setData(barData);
+ leaderboardChart.getXAxis().setValueFormatter(new IndexAxisValueFormatter(teamLabels));
+ leaderboardChart.animateY(800);
+ leaderboardChart.invalidate();
+ }
+
+ /**
+ * Filters all data for a specific team and updates the summary metrics and trend chart.
+ */
+ private void updateDashboard(String teamNum) {
+ List teamMatches = new ArrayList<>();
+ double totalAuto = 0;
+ double totalTeleop = 0;
+ double totalPassing = 0;
+ int climbs = 0;
+
+ for (MatchData m : allMatches) {
+ if (m.teamNum.equals(teamNum)) {
+ teamMatches.add(m);
+ }
+ }
+
+ // Sort matches chronologically by match number
+ teamMatches.sort((m1, m2) -> {
+ try {
+ return Integer.compare(Integer.parseInt(m1.matchNum), Integer.parseInt(m2.matchNum));
+ } catch (Exception e) {
+ return m1.matchNum.compareTo(m2.matchNum);
+ }
+ });
+
+ for (MatchData m : teamMatches) {
+ totalAuto += m.getCalculatedAutoScoredWhole();
+ totalTeleop += m.getCalculatedTeleopScoredWhole();
+ totalPassing += (m.autoPassed + m.teleopPassed);
+ if (m.hangStatus != null && !m.hangStatus.equalsIgnoreCase("None") && !m.hangStatus.equalsIgnoreCase("Nothing")) {
+ climbs++;
+ }
+ }
+
+ if (teamMatches.isEmpty()) return;
+
+ double avgAuto = totalAuto / teamMatches.size();
+ double avgTele = totalTeleop / teamMatches.size();
+ double avgPassing = totalPassing / teamMatches.size();
+ double climbRate = (double) climbs / teamMatches.size() * 100;
+
+ // Update Summary Cards
+ avgAutoText.setText(String.format(Locale.getDefault(), "%.1f", avgAuto));
+ avgTeleopText.setText(String.format(Locale.getDefault(), "%.1f", avgTele));
+ avgPassingText.setText(String.format(Locale.getDefault(), "%.1f", avgPassing));
+ climbRateText.setText(String.format(Locale.getDefault(), "%.0f%%", climbRate));
+
+ updateTrendChart(teamMatches);
+ adapter.updateData(teamMatches);
+ }
+
+ /**
+ * Updates the scoring trend line chart with the robot's match-by-match total score.
+ */
+ private void updateTrendChart(List teamMatches) {
+ List entries = new ArrayList<>();
+ List labels = new ArrayList<>();
+
+ for (int i = 0; i < teamMatches.size(); i++) {
+ MatchData m = teamMatches.get(i);
+ entries.add(new Entry(i, (float) m.getTotalScore()));
+ labels.add("M" + m.matchNum);
+ }
+
+ LineDataSet dataSet = new LineDataSet(entries, "Total Score");
+ dataSet.setColor(ContextCompat.getColor(this, R.color.aztech_blue));
+ dataSet.setCircleColor(ContextCompat.getColor(this, R.color.aztech_yellow));
+ dataSet.setLineWidth(3f);
+ dataSet.setCircleRadius(5f);
+ dataSet.setDrawCircleHole(true);
+ dataSet.setValueTextSize(10f);
+ dataSet.setDrawFilled(true);
+ dataSet.setFillColor(ContextCompat.getColor(this, R.color.aztech_blue_accent));
+ dataSet.setMode(LineDataSet.Mode.CUBIC_BEZIER);
+
+ LineData lineData = new LineData(dataSet);
+ scoringChart.setData(lineData);
+
+ scoringChart.getXAxis().setValueFormatter(new IndexAxisValueFormatter(labels));
+ scoringChart.animateX(800);
+ scoringChart.invalidate();
+ }
+
+ @Override
+ public void onBackPressed() {
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ if (drawer != null && drawer.isDrawerOpen(GravityCompat.START)) {
+ drawer.closeDrawer(GravityCompat.START);
+ } else {
+ super.onBackPressed();
+ }
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/com/example/roboticsscoutingmatchapp/activityPreMatch.java b/app/src/main/java/com/example/roboticsscoutingmatchapp/activityPreMatch.java
index c54e3b5..a3881d4 100644
--- a/app/src/main/java/com/example/roboticsscoutingmatchapp/activityPreMatch.java
+++ b/app/src/main/java/com/example/roboticsscoutingmatchapp/activityPreMatch.java
@@ -10,22 +10,28 @@
import androidx.activity.EdgeToEdge;
+import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
import androidx.core.graphics.Insets;
+import androidx.core.view.GravityCompat;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
+import androidx.drawerlayout.widget.DrawerLayout;
+import com.google.android.material.navigation.NavigationView;
+
+/**
+ * Activity for collecting Pre-Match data (Scouter name, match #, team info).
+ * Updated for the 2026 REBUILT game pieces (Fuel).
+ */
public class activityPreMatch extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
- // Defines an object for the utility file because of weird compat with static methods
U u = new U();
- /*
- Checks for if there is any data sent over with the intent when switching to current
- activity, save strings will be compiled and saved as csv in final activity page
- */
+ // Retrieve existing data strings passed through Intent extras for state persistence
String preMatchSaveString, autoSaveString,
teleOpSaveString, postMatchSaveString, competitionString, scoutNameString;
Bundle extras = getIntent().getExtras();
@@ -45,28 +51,59 @@ protected void onCreate(Bundle savedInstanceState) {
scoutNameString = "";
}
- // Checks for insets changing (screen rotation) -- Auto-generated
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_pre_match);
- ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
+
+ // Sidebar/Navigation Setup
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ NavigationView navigationView = findViewById(R.id.nav_view);
+
+ // Fix status bar overlap for the AppBar and the Navigation Drawer
+ ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.app_bar), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
- v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
+ v.setPadding(0, systemBars.top, 0, 0);
return insets;
});
- // Defining all the relevant components in the activity
+ ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
+ this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
+ drawer.addDrawerListener(toggle);
+ toggle.syncState();
+
+ navigationView.setNavigationItemSelectedListener(item -> {
+ int id = item.getItemId();
+ if (id == R.id.nav_scout) {
+ Intent intent = new Intent(this, ActivityCompetitionSelection.class);
+ intent.putExtra("chooseNewCompetition", true);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
+ } else if (id == R.id.nav_dashboard) {
+ startActivity(new Intent(this, activityDataShowing.class));
+ }
+ drawer.closeDrawer(GravityCompat.START);
+ return true;
+ });
+
+ // UI Component Binding
EditText scoutName = findViewById(R.id.scout_name);
EditText matchNumber = findViewById(R.id.match_number);
EditText teamNumber = findViewById(R.id.team_number);
RadioGroup teamColorRadioGroup = findViewById(R.id.team_color_radio_group);
+
+ // REBUILT 2026 Fix: Resource ID changed from checkBox_preloaded_coral to fuel
CheckBox preloadedFuel = findViewById(R.id.checkBox_preloaded_fuel);
+
Button saveButton = findViewById(R.id.save_button);
Button backButton = findViewById(R.id.back_button);
+
if(!scoutNameString.isEmpty()){
scoutName.setText(scoutNameString);
}
+ // Parse backward-passed data strings to restore UI state
if(!preMatchSaveString.isEmpty()){
competitionString = u.untilNextComma(preMatchSaveString);
preMatchSaveString = u.nextCommaOn(preMatchSaveString); // remove competition
@@ -88,16 +125,14 @@ protected void onCreate(Bundle savedInstanceState) {
}
- // Defines a toast (pop-up) to be used when a field is left unfilled
Toast unfilledMessage = new Toast(this);
unfilledMessage.setDuration(Toast.LENGTH_SHORT);
String finalCompetitionString = competitionString;
saveButton.setOnClickListener((l) -> {
- // Check if all fields are full
-// findViewById(R.id.scroll_view);
String response = "";
+ // Validation logic
if(u.getData(scoutName).isEmpty()){
response = getResources().getString(R.string.prompt_scout_name) + " " + getResources().getString(R.string.is_empty_identifier);
}else if(u.getData(matchNumber).isEmpty()){
@@ -107,10 +142,10 @@ protected void onCreate(Bundle savedInstanceState) {
}else if(u.getData(teamColorRadioGroup).isEmpty()){
response = "Please choose a team color";
}else{
- // Utilizes "savestrings"
+ // Serialize pre-match info into a comma-separated string for passing between activities
Intent i = new Intent(this, activityAutonomous.class);
String preMatchInfo = "";
- preMatchInfo += finalCompetitionString + ","; //TODO: Add competition
+ preMatchInfo += finalCompetitionString + ",";
preMatchInfo += u.DATA_VERSION + ",";
preMatchInfo += u.stripText(u.getData(scoutName), u.DELIMITER_AND_WHITESPACE) + ",";
preMatchInfo += u.stripText(u.getData(teamNumber)) + ",";
@@ -139,4 +174,14 @@ protected void onCreate(Bundle savedInstanceState) {
});
}
-}
\ No newline at end of file
+
+ @Override
+ public void onBackPressed() {
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ if (drawer != null && drawer.isDrawerOpen(GravityCompat.START)) {
+ drawer.closeDrawer(GravityCompat.START);
+ } else {
+ super.onBackPressed();
+ }
+ }
+}
diff --git a/app/src/main/java/com/example/roboticsscoutingmatchapp/activityTeleOp.java b/app/src/main/java/com/example/roboticsscoutingmatchapp/activityTeleOp.java
index 58383ab..a91c7be 100644
--- a/app/src/main/java/com/example/roboticsscoutingmatchapp/activityTeleOp.java
+++ b/app/src/main/java/com/example/roboticsscoutingmatchapp/activityTeleOp.java
@@ -14,6 +14,10 @@
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
+/**
+ * Activity for collecting TeleOperated period data.
+ * Tracks fuel scoring, accuracy position, and endgame climb status.
+ */
public class activityTeleOp extends AppCompatActivity {
@Override
@@ -29,7 +33,8 @@ protected void onCreate(Bundle savedInstanceState) {
U u = new U();
- String preMatchSaveString, autoSaveString, // Gets all savestrings from wherever coming in from
+ // Load intent state
+ String preMatchSaveString, autoSaveString,
teleOpSaveString, postMatchSaveString;
Bundle extras = getIntent().getExtras();
if(extras != null){
@@ -44,12 +49,8 @@ protected void onCreate(Bundle savedInstanceState) {
postMatchSaveString = "";
}
- // Defining all the access-necessary components within the page
- Button backButton = findViewById(R.id.back_button);
- Button saveButton = findViewById(R.id.save_button);
-
+ // UI Binding - Scoring & Accuracy
EditText FSField = findViewById(R.id.edit_text_fs);
-
Button FS1plus = findViewById(R.id.up_count_button_fs1);
Button FS5plus = findViewById(R.id.up_count_button_fs5);
Button FS10plus = findViewById(R.id.up_count_button_fs10);
@@ -69,8 +70,8 @@ protected void onCreate(Bundle savedInstanceState) {
RadioButton seventyFivePercent = findViewById(R.id.seventyFivePercent);
RadioButton overNinetyFive = findViewById(R.id.overNinetyFive);
+ // UI Binding - Passing & Endgame
EditText FPField = findViewById(R.id.edit_text_fp);
-
Button FP1plus = findViewById(R.id.up_count_button_fp1);
Button FP5plus = findViewById(R.id.up_count_button_fp5);
Button FP10plus = findViewById(R.id.up_count_button_fp10);
@@ -87,6 +88,7 @@ protected void onCreate(Bundle savedInstanceState) {
RadioButton level1Button = findViewById(R.id.level1_climb);
RadioButton level2Button = findViewById(R.id.level2_climb);
RadioButton level3Button = findViewById(R.id.level3_climb);
+ RadioButton noneButton = findViewById(R.id.nothing);
RadioGroup accuracyRadioGroup = findViewById(R.id.accuracy_position);
RadioButton pacManButton = findViewById(R.id.pac_manning);
@@ -94,7 +96,6 @@ protected void onCreate(Bundle savedInstanceState) {
RadioButton noDifferenceButton = findViewById(R.id.no_difference);
RadioButton badAccuracyButton = findViewById(R.id.bad_accuracy);
- RadioButton noneButton = findViewById(R.id.nothing);
RadioGroup endgameTimeGroup = findViewById(R.id.endgame_time);
RadioButton twentyFiveButton = findViewById(R.id.twenty_five);
RadioButton twentyButton = findViewById(R.id.twenty);
@@ -103,12 +104,14 @@ protected void onCreate(Bundle savedInstanceState) {
RadioButton fiveButton = findViewById(R.id.five);
RadioButton zeroButton = findViewById(R.id.zero);
+ Button backButton = findViewById(R.id.back_button);
+ Button saveButton = findViewById(R.id.save_button);
+
Toast unfilledMessage = new Toast(this);
unfilledMessage.setDuration(Toast.LENGTH_SHORT);
- // Setting all fields which have data
+ // Parse saved teleop data to restore UI state
if(!teleOpSaveString.isEmpty()){
- // #Fuel Shot | Accuracy % | #Fuel Passed | Hang Status | Hang Time | Accuracy Position |
FSField.setText(u.untilNextComma(teleOpSaveString));
teleOpSaveString = u.nextCommaOn(teleOpSaveString);
FPField.setText(u.untilNextComma(teleOpSaveString));
@@ -116,86 +119,45 @@ protected void onCreate(Bundle savedInstanceState) {
String currentButton = u.untilNextComma(teleOpSaveString);
switch(currentButton){
- case "Level 1":
- level1Button.toggle();
- break;
- case "Level 2":
- level2Button.toggle();
- break;
- case "Level 3":
- level3Button.toggle();
- break;
- case "None":
- noneButton.toggle();
- break;
+ case "Level 1": level1Button.toggle(); break;
+ case "Level 2": level2Button.toggle(); break;
+ case "Level 3": level3Button.toggle(); break;
+ case "None": noneButton.toggle(); break;
}
teleOpSaveString = u.nextCommaOn(teleOpSaveString);
String timeToHang = u.untilNextComma(teleOpSaveString);
switch(timeToHang){
- case "25":
- twentyFiveButton.toggle();
- break;
- case "20":
- twentyButton.toggle();
- break;
- case "15":
- fifteenButton.toggle();
- break;
- case "10":
- tenButton.toggle();
- break;
- case "5":
- fiveButton.toggle();
- break;
- case "0":
- zeroButton.toggle();
- break;
+ case "25": twentyFiveButton.toggle(); break;
+ case "20": twentyButton.toggle(); break;
+ case "15": fifteenButton.toggle(); break;
+ case "10": tenButton.toggle(); break;
+ case "5": fiveButton.toggle(); break;
+ case "0": zeroButton.toggle(); break;
}
teleOpSaveString = u.nextCommaOn(teleOpSaveString);
- String accuracyPosition = u.untilNextComma(teleOpSaveString);
- switch(accuracyPosition){
- case "When moving":
- pacManButton.toggle();
- break;
- case "When unmoving":
- standStillButton.toggle();
- break;
- case "About the same":
- noDifferenceButton.toggle();
- break;
- case "Consistently bad accuracy":
- badAccuracyButton.toggle();
- break;
+ String accuracyPos = u.untilNextComma(teleOpSaveString);
+ switch(accuracyPos){
+ case "When moving": pacManButton.toggle(); break;
+ case "When unmoving": standStillButton.toggle(); break;
+ case "About the same": noDifferenceButton.toggle(); break;
+ case "Consistently bad accuracy": badAccuracyButton.toggle(); break;
}
teleOpSaveString = u.nextCommaOn(teleOpSaveString);
String accuracyChoice = u.untilNextComma(teleOpSaveString);
switch(accuracyChoice){
- case "Less Than 10%":
- lessThanTen.toggle();
- break;
- case "25%":
- twentyFivePercent.toggle();
- break;
- case "50%":
- fiftyPercent.toggle();
- break;
- case "75%":
- seventyFivePercent.toggle();
- break;
- case "More Than 95%":
- overNinetyFive.toggle();
- break;
+ case "Less Than 10%": lessThanTen.toggle(); break;
+ case "25%": twentyFivePercent.toggle(); break;
+ case "50%": fiftyPercent.toggle(); break;
+ case "75%": seventyFivePercent.toggle(); break;
+ case "More Than 95%": overNinetyFive.toggle(); break;
}
-
-
teleOpSaveString = u.nextCommaOn(teleOpSaveString);
-
}
- // Setting increment and decrement listeners for all buttons
+ // Increment/Decrement Listeners for TeleOp Fuel
FS1plus.setOnClickListener((l)->u.incrementText(FSField));
FS1minus.setOnClickListener((l)->u.incrementText(FSField, -1));
FS5plus.setOnClickListener((l)->u.incrementText(FSField, +5));
@@ -218,17 +180,11 @@ protected void onCreate(Bundle savedInstanceState) {
FP20plus.setOnClickListener((l)->u.incrementText(FPField, +20));
FP20minus.setOnClickListener((l)->u.incrementText(FPField, -20));
-
- // Back button, which sends data backwards even if it's unfilled
backButton.setOnClickListener((l)->{
String teleOpInfo = "";
- // #Fuel Shot | Accuracy % | #Fuel Passed | Hang Status | Hang Time | Accuracy Position |
-
teleOpInfo += u.getData(FSField) + ",";
teleOpInfo += u.getData(accuracyGroup) + ",";
-
teleOpInfo += u.getData(FPField) + ",";
-
teleOpInfo += u.getData(parkRadioGroup) + ",";
teleOpInfo += u.getData(endgameTimeGroup) + ",";
teleOpInfo += u.getData(accuracyRadioGroup) + ",";
@@ -238,32 +194,27 @@ protected void onCreate(Bundle savedInstanceState) {
i.putExtra("auto", autoSaveString);
i.putExtra("teleOp", teleOpInfo);
i.putExtra("postMatch", postMatchSaveString);
-
this.startActivity(i);
});
saveButton.setOnClickListener((l) -> {
String response = "";
- if (u.getData(FSField).isEmpty())
- FSField.setText("0");
- if(u.getData(FPField).isEmpty())
- FPField.setText("0");
+ if (u.getData(FSField).isEmpty()) FSField.setText("0");
+ if(u.getData(FPField).isEmpty()) FPField.setText("0");
+
if ((u.getData(accuracyGroup).isEmpty()))
response = "Please give accuracy";
- if ((u.getData(accuracyRadioGroup).isEmpty()))
+ else if ((u.getData(accuracyRadioGroup).isEmpty()))
response = "Please give accuracy position";
- if(u.getData(parkRadioGroup).isEmpty())
+ else if(u.getData(parkRadioGroup).isEmpty())
response = "Please select an endgame position";
else if(u.getData(endgameTimeGroup).isEmpty())
response = "Please select park time";
else{
String teleOpInfo = "";
-
teleOpInfo += u.getData(FSField) + ",";
teleOpInfo += u.getData(accuracyGroup) + ",";
-
teleOpInfo += u.getData(FPField) + ",";
-
teleOpInfo += u.getData(parkRadioGroup) + ",";
teleOpInfo += u.getData(endgameTimeGroup) + ",";
teleOpInfo += u.getData(accuracyRadioGroup) + ",";
@@ -273,7 +224,6 @@ else if(u.getData(endgameTimeGroup).isEmpty())
i.putExtra("auto", autoSaveString);
i.putExtra("teleOp", teleOpInfo);
i.putExtra("postMatch", postMatchSaveString);
-
this.startActivity(i);
}
if(!response.isEmpty()){
@@ -282,4 +232,4 @@ else if(u.getData(endgameTimeGroup).isEmpty())
}
});
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/res/layout/activity_after_match.xml b/app/src/main/res/layout/activity_after_match.xml
index 5bcba50..4d3d9e1 100644
--- a/app/src/main/res/layout/activity_after_match.xml
+++ b/app/src/main/res/layout/activity_after_match.xml
@@ -326,4 +326,4 @@
-
\ No newline at end of file
+
diff --git a/app/src/main/res/layout/activity_competition_selection.xml b/app/src/main/res/layout/activity_competition_selection.xml
index 21d55a9..efdb21b 100644
--- a/app/src/main/res/layout/activity_competition_selection.xml
+++ b/app/src/main/res/layout/activity_competition_selection.xml
@@ -1,165 +1,113 @@
-
-
+
+
-
+
+
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
+
+
+ android:orientation="vertical"
+ android:gravity="start"
+ android:divider="@drawable/vertical_padding_small"
+ android:showDividers="middle">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
-
\ No newline at end of file
+
diff --git a/app/src/main/res/layout/activity_data_showing.xml b/app/src/main/res/layout/activity_data_showing.xml
index 42d5a79..c8f9705 100644
--- a/app/src/main/res/layout/activity_data_showing.xml
+++ b/app/src/main/res/layout/activity_data_showing.xml
@@ -1,71 +1,355 @@
-
-
+
+
-
+
+
+ app:layout_constraintTop_toTopOf="parent">
+
+
+
+
+ android:layout_height="0dp"
+ app:layout_constraintTop_toBottomOf="@id/app_bar"
+ app:layout_constraintBottom_toBottomOf="parent">
+
-
-
+
-
+ android:layout_margin="16dp"
+ app:cardCornerRadius="8dp"
+ app:cardElevation="2dp"
+ app:cardBackgroundColor="@color/aztech_blue_accent">
+ android:orientation="vertical"
+ android:padding="16dp">
+
+ android:text="Leaderboard"
+ android:textStyle="bold"
+ android:textSize="20sp"
+ android:textColor="@color/aztech_blue"
+ android:gravity="center"
+ android:layout_marginBottom="12dp"/>
+
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="center"
+ android:layout_marginBottom="12dp">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
-
\ No newline at end of file
+
diff --git a/app/src/main/res/layout/activity_pre_match.xml b/app/src/main/res/layout/activity_pre_match.xml
index 5e9ce55..1a1cbe3 100644
--- a/app/src/main/res/layout/activity_pre_match.xml
+++ b/app/src/main/res/layout/activity_pre_match.xml
@@ -1,203 +1,210 @@
-
-
+
+
+ tools:context=".activityPreMatch">
-
-
-
-
-
-
-
-
+ app:layout_constraintTop_toTopOf="parent">
-
-
-
-
-
-
+
+
+
-
-
-
+ android:layout_height="0dp"
+ app:layout_constraintTop_toBottomOf="@id/app_bar"
+ app:layout_constraintBottom_toBottomOf="parent">
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+ android:textColor="@color/black"
+ android:gravity="center"/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_match_history.xml b/app/src/main/res/layout/item_match_history.xml
new file mode 100644
index 0000000..b7f8ef5
--- /dev/null
+++ b/app/src/main/res/layout/item_match_history.xml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/nav_header.xml b/app/src/main/res/layout/nav_header.xml
new file mode 100644
index 0000000..f525f34
--- /dev/null
+++ b/app/src/main/res/layout/nav_header.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/menu/nav_menu.xml b/app/src/main/res/menu/nav_menu.xml
new file mode 100644
index 0000000..2b9fbc2
--- /dev/null
+++ b/app/src/main/res/menu/nav_menu.xml
@@ -0,0 +1,13 @@
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 23d41d2..f044702 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -161,6 +161,7 @@
Heavy Defense
Save
+ View Dashboard
Human Player Page!
@@ -176,5 +177,7 @@
Processor
Data
+ Open navigation drawer
+ Close navigation drawer
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index b8f0f66..f761621 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -1,6 +1,6 @@
-
-
\ No newline at end of file
+
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index e60358e..7bf154e 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -7,6 +7,7 @@ appcompat = "1.7.0"
material = "1.12.0"
activity = "1.10.0"
constraintlayout = "2.2.0"
+mpandroidchart = "v3.1.0"
[libraries]
junit = { group = "junit", name = "junit", version.ref = "junit" }
@@ -16,6 +17,8 @@ appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "a
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
+mpandroidchart = { group = "com.github.PhilJay", name = "MPAndroidChart", version.ref = "mpandroidchart" }
+
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 6b0408e..421cbb7 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -16,6 +16,7 @@ dependencyResolutionManagement {
repositories {
google()
mavenCentral()
+ maven { url = uri("https://jitpack.io") }
}
}