Add scout notice, start work on scouting report system

This commit is contained in:
Michael Mikovsky
2025-04-11 22:07:41 -06:00
parent e559b4dac9
commit e280fc8523
18 changed files with 346 additions and 19 deletions
@@ -73,7 +73,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();
@@ -120,6 +120,18 @@ public class frcEvent {
return maxMatch; return maxMatch;
} }
public frcMatch getNextTeamMatch(int teamNum, int curMatch){
frcMatch[] teamMatches = getTeamMatches(teamNum);
for(int i = 0; i < teamMatches.length; i++) {
if (teamMatches[i].matchIndex > curMatch)
return teamMatches[i];
}
return null;
}
// public // public
// Returns the soonest match that there will be all the possible upcoming data on other teams // Returns the soonest match that there will be all the possible upcoming data on other teams
@@ -144,4 +156,13 @@ public class frcEvent {
} }
} }
} }
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;
}
} }
@@ -200,8 +200,13 @@ public class CandlestickView extends View {
teamDataArray[i] = (int) teamData.get(i).get(); teamDataArray[i] = (int) teamData.get(i).get();
} }
float lowerQuartile = DataProcessing.calculatePercentile(teamDataArray, 25); float lowerQuartile = 0;
float upperQuartile = DataProcessing.calculatePercentile(teamDataArray, 75); float upperQuartile = 0;
if(teamDataArray.length != 0) {
lowerQuartile = DataProcessing.calculatePercentile(teamDataArray, 25);
upperQuartile = DataProcessing.calculatePercentile(teamDataArray, 75);
}
System.out.println(locmin + ", " + lowerQuartile + ", " + avg + ", " + upperQuartile + ", " + locmax); System.out.println(locmin + ", " + lowerQuartile + ", " + avg + ", " + upperQuartile + ", " + locmax);
setData(locmin, lowerQuartile, avg, upperQuartile, locmax, absmin, absmax); setData(locmin, lowerQuartile, avg, upperQuartile, locmax, absmin, absmax);
@@ -20,6 +20,7 @@ import android.view.ViewGroup;
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 androidx.fragment.app.FragmentTransaction;
import com.ridgebotics.ridgescout.R; import com.ridgebotics.ridgescout.R;
import com.ridgebotics.ridgescout.types.frcTeam; import com.ridgebotics.ridgescout.types.frcTeam;
@@ -84,9 +85,6 @@ public class DataFragment extends Fragment {
} }
public void load_teams(){ public void load_teams(){
DataManager.reload_event();
if(event == null) return;
int[] teamNums = new int[event.teams.size()]; int[] teamNums = new int[event.teams.size()];
@@ -113,7 +111,8 @@ public class DataFragment extends Fragment {
frcTeam finalTeam = team; frcTeam finalTeam = team;
teamRow.setOnClickListener(v -> { teamRow.setOnClickListener(v -> {
TeamsFragment.setTeam(finalTeam); TeamsFragment.setTeam(finalTeam);
findNavController(this).navigate(R.id.action_navigation_data_to_navigation_data_teams); ((DataParentFragment) getParentFragment()).moveToFragment(new TeamsFragment());
// findNavController(this).navigate(R.id.action_navigation_data_parent_to_navigation_data_teams);
}); });
} }
} }
@@ -131,7 +130,8 @@ public class DataFragment extends Fragment {
final int fi = i; final int fi = i;
tr.setOnClickListener(v -> { tr.setOnClickListener(v -> {
FieldDataFragment.setFieldIndex(fi); FieldDataFragment.setFieldIndex(fi);
findNavController(this).navigate(R.id.action_navigation_data_to_navigation_data_field_data); ((DataParentFragment) getParentFragment()).moveToFragment(new FieldDataFragment());
// findNavController(get).navigate(R.id.action_navigation_data_parent_to_navigation_data_field_data);
}); });
} }
} }
@@ -0,0 +1,129 @@
package com.ridgebotics.ridgescout.ui.data;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static androidx.navigation.fragment.FragmentKt.findNavController;
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.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.ui.FieldBorderedRow;
import com.ridgebotics.ridgescout.ui.TeamListOption;
import com.ridgebotics.ridgescout.utility.DataManager;
import com.ridgebotics.ridgescout.utility.SettingsManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class DataParentFragment extends Fragment {
private FragmentDataParentBinding binding;
private DataFragment dataFragment;
private boolean editBoxEnabled = true;
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();
}
binding.reportToggleButton.setOnClickListener(view -> {
editBoxEnabled =! editBoxEnabled;
binding.ScoutingEditBox.setVisibility(editBoxEnabled ? GONE : VISIBLE);
binding.reportToggleButton.setText(editBoxEnabled ? "▲ report" : "▼ report");
});
generateScoutingTemplate(SettingsManager.getMatchNum());
return binding.getRoot();
}
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
public void generateScoutingTemplate(int currentMatch){
int teamNum = SettingsManager.getTeamNum();
boolean isBlueAlliance = false;
frcMatch nextMatch = event.getNextTeamMatch(teamNum, currentMatch);
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+1) + "\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));
binding.scoutingReportEdittext.setText(output);
}
private static String getTeamNameAndNum(frcTeam team){
return "\n" + team.teamNumber + " " + team.teamName + ": \n";
}
}
@@ -109,6 +109,11 @@ public class MatchScoutingFragment extends Fragment {
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();
} }
@@ -56,6 +56,10 @@ public class PitScoutingFragment extends Fragment {
return binding.getRoot(); return binding.getRoot();
} }
if(DataManager.scoutNotice.isEmpty())
binding.scoutingNoticeBox.setVisibility(View.GONE);
else
binding.scoutingNoticeText.setText(DataManager.scoutNotice);
loadTeam(); loadTeam();
@@ -124,16 +124,11 @@ public class ScoutingFragment extends Fragment {
binding.textName.setText("Welcome, " + SettingsManager.getUsername() + "!"); binding.textName.setText("Welcome, " + SettingsManager.getUsername() + "!");
int nextMatch = -1; int matchNum = SettingsManager.getMatchNum();
int teamNum = SettingsManager.getTeamNum(); int nextMatch = event.getNextTeamMatch(SettingsManager.getTeamNum(), matchNum).matchIndex;
for(int i = SettingsManager.getMatchNum(); i < event.matches.size(); i++){ // Loop through matches and find next match
boolean foundMatch = false;
for(int a = 0; a < 3; a++) if(event.matches.get(i).blueAlliance[a] == teamNum){nextMatch = i+1; foundMatch = true;}
for(int a = 0; a < 3; a++) if(event.matches.get(i).redAlliance[a] == teamNum){nextMatch = i+1; foundMatch = true;}
if(foundMatch) break;
}
binding.textNextMatch.setText("Our next match: Match " + nextMatch); binding.textNextMatch.setText("Our next match: Match " + nextMatch);
binding.textMatchAlliance.setText("Match: " + (SettingsManager.getMatchNum()+1) + ", " + SettingsManager.getAllyPos()); binding.textMatchAlliance.setText("Match: " + (matchNum+1) + ", " + SettingsManager.getAllyPos());
binding.textRescoutIndicator.setText("Things to rescout: " + DataManager.rescout_list.size()); binding.textRescoutIndicator.setText("Things to rescout: " + DataManager.rescout_list.size());
return binding.getRoot(); return binding.getRoot();
@@ -16,7 +16,9 @@ import static com.ridgebotics.ridgescout.utility.SettingsManager.defaults;
import static com.ridgebotics.ridgescout.utility.SettingsManager.getEditor; import static com.ridgebotics.ridgescout.utility.SettingsManager.getEditor;
import static com.ridgebotics.ridgescout.utility.SettingsManager.prefs; import static com.ridgebotics.ridgescout.utility.SettingsManager.prefs;
import android.app.AlertDialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable; import android.text.Editable;
import android.text.InputType; import android.text.InputType;
@@ -25,7 +27,10 @@ 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.Button;
import android.widget.EditText;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@@ -130,6 +135,14 @@ public class SettingsFragment extends Fragment {
binding.SettingsTable.removeAllViews(); binding.SettingsTable.removeAllViews();
manager.getView(binding.SettingsTable); manager.getView(binding.SettingsTable);
if(!DataManager.getevcode().equals("unset")){
Button editNoticeButton = new Button(getContext());
editNoticeButton.setText("Edit Scout Notice");
binding.SettingsTable.addView(editNoticeButton);
editNoticeButton.setOnClickListener(v->editNotice());
}
} }
@@ -141,6 +154,27 @@ public class SettingsFragment extends Fragment {
} }
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();
}
@@ -25,8 +25,9 @@ public class DataManager {
SettingsManager.setEVCode("unset"); SettingsManager.setEVCode("unset");
evcode = "unset"; evcode = "unset";
}else{ }else{
AlertManager.toast("Reloaded event!");
reload_rescout_list(); reload_rescout_list();
reload_scout_notice();
AlertManager.toast("Reloaded event!");
} }
} }
@@ -90,4 +91,36 @@ public class DataManager {
AlertManager.error("Error saving scout fields", e); AlertManager.error("Error saving scout fields", 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);
rescout_list = new ArrayList<>();
}
}
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);
}
}
} }
@@ -28,6 +28,7 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:paddingBottom="50dp"
android:orientation="vertical"> android:orientation="vertical">
<TableLayout <TableLayout
@@ -16,6 +16,7 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:paddingBottom="50dp"
android:orientation="vertical"> android:orientation="vertical">
<TableLayout <TableLayout
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/data_subfragment"
android:name="com.ridgebotics.ridgescout.ui.data.DataFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/ScoutingEditBox"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</FrameLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/ScoutingEditBox"
android:layout_width="match_parent"
android:layout_height="200dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
tools:visibility="visible">
<EditText
android:id="@+id/scouting_report_edittext"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<Button
android:id="@+id/report_toggle_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="▲ report"
android:textSize="15sp"
app:layout_constraintBottom_toTopOf="@+id/ScoutingEditBox"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -14,6 +14,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
android:paddingBottom="50dp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
@@ -18,6 +18,26 @@
android:orientation="vertical" android:orientation="vertical"
android:paddingTop="48dp"> android:paddingTop="48dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/scouting_notice_box"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_margin="5dp"
android:background="@drawable/border"
android:padding="10dp"
android:orientation="horizontal">
<TextView
android:id="@+id/scouting_notice_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
android:text="Scouting Notice">
</TextView>
</androidx.constraintlayout.widget.ConstraintLayout>
<com.ridgebotics.ridgescout.ui.TeamCard <com.ridgebotics.ridgescout.ui.TeamCard
android:id="@+id/match_team_card" android:id="@+id/match_team_card"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -53,6 +53,26 @@
android:orientation="vertical" android:orientation="vertical"
android:paddingTop="48dp"> android:paddingTop="48dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/scouting_notice_box"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_margin="5dp"
android:background="@drawable/border"
android:padding="10dp"
android:orientation="horizontal">
<TextView
android:id="@+id/scouting_notice_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
android:text="Scouting Notice">
</TextView>
</androidx.constraintlayout.widget.ConstraintLayout>
<com.ridgebotics.ridgescout.ui.TeamCard <com.ridgebotics.ridgescout.ui.TeamCard
android:id="@+id/pits_team_card" android:id="@+id/pits_team_card"
android:layout_width="wrap_content" android:layout_width="wrap_content"
+1 -1
View File
@@ -6,7 +6,7 @@
android:icon="@drawable/scouting" android:icon="@drawable/scouting"
android:title="@string/title_scouting" /> android:title="@string/title_scouting" />
<item <item
android:id="@+id/navigation_data" android:id="@+id/navigation_data_parent"
android:icon="@drawable/data" android:icon="@drawable/data"
android:title="@string/title_data" /> android:title="@string/title_data" />
<item <item
@@ -89,6 +89,18 @@
<fragment
android:id="@+id/navigation_data_parent"
android:name="com.ridgebotics.ridgescout.ui.data.DataParentFragment"
android:label="@string/title_data"
tools:layout="@layout/fragment_data">
<action
android:id="@+id/action_navigation_data_parent_to_navigation_data_field_data"
app:destination="@id/navigation_data_field_data" />
<action
android:id="@+id/action_navigation_data_parent_to_navigation_data_teams"
app:destination="@id/navigation_data_teams" />
</fragment>
<fragment <fragment
android:id="@+id/navigation_data" android:id="@+id/navigation_data"