diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 26e7178..a264ec4 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -27,7 +27,7 @@ jobs: run: ./gradlew build - name: Upload APK - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: build-apk path: ./**/*.apk \ No newline at end of file diff --git a/README.md b/README.md index 5ff50f2..b13b430 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,6 @@ Ridgebotics 2025 scouting app in Android ## In Progress: #### Scouting: #### Data Analysis: -- Add "history" view type to the teams view menu. -- Sentiment analysis of text input type - Make a word cloud for the compiled mode of text input type #### Functionality: - Make pit and match data field builder UIs. I don't want to have to keep editing a variable @@ -30,6 +28,8 @@ Ridgebotics 2025 scouting app in Android #### Scouting: - Add an "unselect" option to all of the scouting fields #### Data Analysis: +- Add "history" view type to the teams view menu. +- Sentiment analysis of text input type #### Functionality: - Make the file browser UI - Bluetooth data sync diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d8451c0..4cc0f5e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,5 +1,9 @@ +import com.android.build.api.dsl.AaptOptions +import com.android.build.api.dsl.AndroidResources + plugins { alias(libs.plugins.androidApplication) +// id("com.google.gms.google-services") } //allprojects { @@ -35,6 +39,9 @@ android { buildFeatures { viewBinding = true } + aaptOptions { + noCompress("tflite"); + } } dependencies { @@ -63,7 +70,16 @@ dependencies { implementation("com.github.skydoves:powerspinner:1.2.7") implementation("com.github.PhilJay:MPAndroidChart:v3.1.0") - implementation("com.github.douglasjunior:AndroidBluetoothLibrary:0.4.0") + + + +// implementation("com.google.firebase:firebase-ml-modeldownloader:24.1.2") +// implementation(platform("com.google.firebase:firebase-bom:33.1.2")) + + implementation("org.tensorflow:tensorflow-lite-task-text:0.3.0") + + + // implementation("com.github.DeveloperPaul123:SimpleBluetoothLibrary:1.5.1") } diff --git a/app/src/main/assets/text_classification_v2.tflite b/app/src/main/assets/text_classification_v2.tflite new file mode 100644 index 0000000..3335214 Binary files /dev/null and b/app/src/main/assets/text_classification_v2.tflite differ diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/MainActivity.java b/app/src/main/java/com/astatin3/scoutingapp2025/MainActivity.java index d56cec2..941c6df 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/MainActivity.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/MainActivity.java @@ -1,16 +1,14 @@ package com.astatin3.scoutingapp2025; -import android.app.AlertDialog; -import android.content.Context; import android.os.Bundle; import com.astatin3.scoutingapp2025.scoutingData.fields; +import com.astatin3.scoutingapp2025.ui.data.sentimentAnalysis; import com.astatin3.scoutingapp2025.utility.AlertManager; import com.astatin3.scoutingapp2025.utility.fileEditor; import com.google.android.material.bottomnavigation.BottomNavigationView; import androidx.appcompat.app.AppCompatActivity; -import androidx.fragment.app.Fragment; import androidx.navigation.NavController; import androidx.navigation.Navigation; import androidx.navigation.ui.AppBarConfiguration; @@ -43,6 +41,7 @@ public class MainActivity extends AppCompatActivity { } AlertManager.init(this); + sentimentAnalysis.init(this); Objects.requireNonNull(getSupportActionBar()).hide(); diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/SettingsVersionStack/sv1.java b/app/src/main/java/com/astatin3/scoutingapp2025/SettingsVersionStack/sv1.java index 1c057ec..3252906 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/SettingsVersionStack/sv1.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/SettingsVersionStack/sv1.java @@ -28,7 +28,7 @@ public class sv1 extends sv0 { writeTag("match_num", "0"); writeTag("alliance_pos", "red-1"); - writeTag("compiled_mode", "false"); + writeTag("data_view_mode", "0"); writeTag("bt_uuid", UUID.randomUUID().toString()); } @@ -66,12 +66,12 @@ public class sv1 extends sv0 { } public void set_team_num(int num){writeTag("team_num", String.valueOf(num));} - public boolean get_compiled_mode(){ - return readTag("compiled_mode").equals("true"); + public int get_data_view_mode(){ + return Integer.parseInt(readTag("data_view_mode")); } - public void set_compiled_mode(boolean state){ - writeTag("compiled_mode", state ? "true" : "false"); + public void set_data_view_mode(int mode){ + writeTag("data_view_mode", String.valueOf(mode)); } public void setUUID(UUID uuid){ diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/types/input/dropdownType.java b/app/src/main/java/com/astatin3/scoutingapp2025/types/input/dropdownType.java index 2f47d24..b67f663 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/types/input/dropdownType.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/types/input/dropdownType.java @@ -15,14 +15,19 @@ import com.astatin3.scoutingapp2025.R; import com.astatin3.scoutingapp2025.types.data.dataType; import com.astatin3.scoutingapp2025.types.data.intType; import com.astatin3.scoutingapp2025.types.data.stringType; +import com.astatin3.scoutingapp2025.ui.data.sentimentAnalysis; import com.astatin3.scoutingapp2025.utility.BuiltByteParser; import com.astatin3.scoutingapp2025.utility.ByteBuilder; import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.charts.PieChart; +import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.LineDataSet; import com.github.mikephil.charting.data.PieData; import com.github.mikephil.charting.data.PieDataSet; import com.github.mikephil.charting.data.PieEntry; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.skydoves.powerspinner.IconSpinnerAdapter; import com.skydoves.powerspinner.IconSpinnerItem; import com.skydoves.powerspinner.OnSpinnerItemSelectedListener; @@ -46,6 +51,11 @@ public class dropdownType extends inputType { this.text_options = text_options; this.default_value = defaultSelIndex; } + + + + + public byte[] encode() throws ByteBuilder.buildingException { ByteBuilder bb = new ByteBuilder(); bb.addString(name); @@ -62,6 +72,10 @@ public class dropdownType extends inputType { text_options = (String[]) objects.get(2).get(); } + + + + public PowerSpinnerView dropdown = null; public View createView(Context context, Function onUpdate){ @@ -126,6 +140,12 @@ public class dropdownType extends inputType { if(dropdown.getVisibility() == View.GONE) return new intType(name, intType.nulval); return new intType(name, dropdown.getSelectedIndex()); } + + + + + + public void add_individual_view(LinearLayout parent, dataType data){ if(data.isNull()) return; TextView tv = new TextView(parent.getContext()); @@ -139,6 +159,14 @@ public class dropdownType extends inputType { tv.setTextSize(18); parent.addView(tv); } + + + + + + + + private static int[] generateEquidistantColors(int N) { int[] colors = new int[N]; float[] hsv = new float[3]; // Hue, Saturation, Value @@ -182,5 +210,76 @@ public class dropdownType extends inputType { chart.setDrawHoleEnabled(false); chart.setData(pieData); } + + + + + + + public void add_history_view(LinearLayout parent, dataType[] data){ + LineChart chart = new LineChart(parent.getContext()); + FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ); + layout.height = 350; + chart.setLayoutParams(layout); + chart.setBackgroundColor(0xff252025); + + + + int[] colors = generateEquidistantColors(text_options.length); + + LineData lineData = new LineData(); + + for(int i = 0; i < text_options.length; i++){ + List entries = new ArrayList<>(); + for (int a = 0; a < data.length; a++) { + if(data[a] == null) continue; + + entries.add( + new Entry(a, + ((int) data[a].get()) == i ? 1.f : 0.f + ) + ); + } + + LineDataSet dataSet = new LineDataSet(entries, text_options[i]); + dataSet.setColor(colors[i]); + dataSet.setValueTextColor(Color.BLACK); + dataSet.setDrawCircles(false); + dataSet.setDrawValues(false); + dataSet.setValueTextColor(Color.RED); + lineData.addDataSet(dataSet); + } + + + + + chart.setData(lineData); + chart.invalidate(); + + chart.getDescription().setEnabled(false); + chart.setTouchEnabled(false); + chart.setDragEnabled(false); + chart.setScaleEnabled(false); + + + chart.getXAxis().setTextColor(Color.WHITE); + chart.getAxisLeft().setTextColor(Color.WHITE); + chart.getAxisRight().setTextColor(Color.WHITE); + + chart.getAxisLeft().setAxisMinimum(0.f); + chart.getAxisLeft().setAxisMaximum(1.f); + + chart.getAxisRight().setAxisMinimum(0.f); + chart.getAxisRight().setAxisMaximum(1.f); + + Legend legend = chart.getLegend(); + legend.setTextColor(Color.WHITE); + + chart.invalidate(); + parent.addView(chart); + } } diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/types/input/inputType.java b/app/src/main/java/com/astatin3/scoutingapp2025/types/input/inputType.java index 667376d..6724cd2 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/types/input/inputType.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/types/input/inputType.java @@ -33,16 +33,26 @@ public abstract class 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 View createView(Context context, Function 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(); - public abstract void add_individual_view(LinearLayout parent, dataType data); - public abstract void add_compiled_view(LinearLayout parent, dataType[] data); - public abstract String get_type_name(); + + 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); } \ No newline at end of file diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/types/input/sliderType.java b/app/src/main/java/com/astatin3/scoutingapp2025/types/input/sliderType.java index 8152f20..adcfc65 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/types/input/sliderType.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/types/input/sliderType.java @@ -41,6 +41,10 @@ public class sliderType extends inputType { this.min = min; this.max = max; } + + + + public byte[] encode() throws ByteBuilder.buildingException { ByteBuilder bb = new ByteBuilder(); bb.addString(name); @@ -59,6 +63,9 @@ public class sliderType extends inputType { max = (int) objects.get(3).get(); } + + + public Slider slider = null; public View createView(Context context, Function onUpdate){ @@ -96,6 +103,12 @@ public class sliderType extends inputType { isBlank = true; slider.setVisibility(View.GONE); } + + + + + + public void add_individual_view(LinearLayout parent, dataType data){ if(data.isNull()) return; Slider slider = new Slider(parent.getContext()); @@ -111,6 +124,12 @@ public class sliderType extends inputType { } + + + + + + private float calculateMean(int[] data) { float sum = 0; for (int value : data) { @@ -138,8 +157,6 @@ public class sliderType extends inputType { return entries; } - - public void add_compiled_view(LinearLayout parent, dataType[] data){ LineChart chart = new LineChart(parent.getContext()); FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams( @@ -191,13 +208,8 @@ public class sliderType extends inputType { normalDistSet.setDrawValues(false); normalDistSet.setLineWidth(2f); - - - LineData lineData = new LineData(dataSet, normalDistSet); - - chart.setData(lineData); chart.invalidate(); @@ -217,4 +229,61 @@ public class sliderType extends inputType { parent.addView(chart); } + + + + + public void add_history_view(LinearLayout parent, dataType[] data){ + LineChart chart = new LineChart(parent.getContext()); + FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ); + layout.height = 350; + chart.setLayoutParams(layout); + chart.setBackgroundColor(0xff252025); + + List entries = new ArrayList<>(); + for (int i = 0; i < data.length; i++){ + if(data[i] == null) continue; + + entries.add(new Entry(i, (float)(int) data[i].get())); + } + + + LineDataSet dataSet = new LineDataSet(entries, name); + dataSet.setColor(Color.BLUE); + dataSet.setValueTextColor(Color.BLACK); + dataSet.setDrawCircles(false); + dataSet.setDrawValues(false); + + LineData lineData = new LineData(dataSet); + + chart.setData(lineData); + chart.invalidate(); + + chart.getDescription().setEnabled(false); + chart.setTouchEnabled(false); + chart.setDragEnabled(false); + chart.setScaleEnabled(false); + + dataSet.setValueTextColor(Color.RED); + + chart.getXAxis().setTextColor(Color.WHITE); + chart.getAxisLeft().setTextColor(Color.WHITE); + chart.getAxisRight().setTextColor(Color.WHITE); + + Legend legend = chart.getLegend(); + legend.setTextColor(Color.WHITE); + + + chart.getAxisLeft().setAxisMinimum(min); + chart.getAxisLeft().setAxisMaximum(max); + + chart.getAxisRight().setAxisMinimum(min); + chart.getAxisRight().setAxisMaximum(max); + + + parent.addView(chart); + } } \ No newline at end of file diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/types/input/textType.java b/app/src/main/java/com/astatin3/scoutingapp2025/types/input/textType.java index 08443df..ae41e40 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/types/input/textType.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/types/input/textType.java @@ -1,6 +1,7 @@ package com.astatin3.scoutingapp2025.types.input; import android.content.Context; +import android.graphics.Color; import android.text.Editable; import android.text.TextWatcher; import android.view.Gravity; @@ -13,10 +14,18 @@ import android.widget.TextView; import com.astatin3.scoutingapp2025.types.data.dataType; import com.astatin3.scoutingapp2025.types.data.stringType; +import com.astatin3.scoutingapp2025.ui.data.sentimentAnalysis; +import com.astatin3.scoutingapp2025.utility.AlertManager; import com.astatin3.scoutingapp2025.utility.BuiltByteParser; import com.astatin3.scoutingapp2025.utility.ByteBuilder; +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 java.util.ArrayList; +import java.util.List; import java.util.function.Function; public class textType extends inputType { @@ -30,6 +39,13 @@ public class textType extends inputType { this.default_value = default_text; } public String get_type_name(){return "Text";} + + + + + + + public byte[] encode() throws ByteBuilder.buildingException { ByteBuilder bb = new ByteBuilder(); bb.addString(name); @@ -44,6 +60,19 @@ public class textType extends inputType { default_value = objects.get(1).get(); } + + + + + + + + + + + + + public EditText text = null; public View createView(Context context, Function onUpdate){ @@ -78,6 +107,10 @@ public class textType extends inputType { if(text.getVisibility() == View.GONE) return new stringType(name, stringType.nulval); return new stringType(name, text.getText().toString()); } + + + + public void add_individual_view(LinearLayout parent, dataType data){ if(data.isNull()) return; TextView tv = new TextView(parent.getContext()); @@ -90,17 +123,109 @@ public class textType extends inputType { tv.setTextSize(18); parent.addView(tv); } - public void add_compiled_view(LinearLayout parent, dataType[] data){ -// if(data.get().equals(stringType.nulval)) return; - TextView tv = new TextView(parent.getContext()); - tv.setLayoutParams(new FrameLayout.LayoutParams( + + + + + + + + + + + + + + float positive_mean = 0; + int count = 0; + + TextView positive_text; + + public void add_compiled_view(LinearLayout parent, dataType[] data) { + positive_mean = 0; + count = 0; + + positive_text = new TextView(parent.getContext()); + positive_text.setLayoutParams(new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT )); - tv.setGravity(Gravity.CENTER_HORIZONTAL); - tv.setText(""); - tv.setTextSize(20); - parent.addView(tv); + positive_text.setGravity(Gravity.CENTER_HORIZONTAL); + positive_text.setTextSize(20); + parent.addView(positive_text); + + for (int i = 0; i < data.length; i++){ + if (!data[i].isNull()) { + sentimentAnalysis.analyse((String) data[i].get(), new sentimentAnalysis.resultCallback() { + @Override + public void onFinish(float sentiment) { + positive_mean += sentiment; + count++; + + positive_text.setText("Sentiment: " + (positive_mean / count)); + } + }); + } + } + } + + + 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); + + + List entries = new ArrayList<>(); + for (int i = 0; i < data.length; i++) { + if(data[i] == null) continue; + + entries.add( + new Entry(i, + sentimentAnalysis.analyse_sync( (String) data[i].get() ) + ) + ); + } + LineDataSet dataSet = new LineDataSet(entries, "Sentiment"); + dataSet.setColor(Color.BLUE); + dataSet.setValueTextColor(Color.BLACK); + dataSet.setDrawCircles(false); + dataSet.setDrawValues(false); + dataSet.setValueTextColor(Color.RED); + + + LineData lineData = new LineData(dataSet); + + chart.setData(lineData); + chart.invalidate(); + + chart.getDescription().setEnabled(false); + chart.setTouchEnabled(false); + chart.setDragEnabled(false); + chart.setScaleEnabled(false); + + + chart.getXAxis().setTextColor(Color.WHITE); + chart.getAxisLeft().setTextColor(Color.WHITE); + chart.getAxisRight().setTextColor(Color.WHITE); + + chart.getAxisLeft().setAxisMinimum(0.f); + chart.getAxisLeft().setAxisMaximum(1.f); + + chart.getAxisRight().setAxisMinimum(0.f); + chart.getAxisRight().setAxisMaximum(1.f); + + Legend legend = chart.getLegend(); + legend.setTextColor(Color.WHITE); + + chart.invalidate(); + parent.addView(chart); + } } diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/ui/data/sentimentAnalysis.java b/app/src/main/java/com/astatin3/scoutingapp2025/ui/data/sentimentAnalysis.java new file mode 100644 index 0000000..e6c6c1a --- /dev/null +++ b/app/src/main/java/com/astatin3/scoutingapp2025/ui/data/sentimentAnalysis.java @@ -0,0 +1,57 @@ +package com.astatin3.scoutingapp2025.ui.data; + +import android.content.Context; + +import com.astatin3.scoutingapp2025.utility.AlertManager; + +import org.tensorflow.lite.support.label.Category; +import org.tensorflow.lite.task.text.nlclassifier.NLClassifier; + +import java.util.List; + +public class sentimentAnalysis { + private static NLClassifier textClassifier; + + public static void init(Context context){ + try { + textClassifier = NLClassifier.createFromFile(context, "text_classification_v2.tflite"); + } catch (Exception e) { + AlertManager.error(e); + } + } + + public interface resultCallback { + public void onFinish(float data); + } + + public static void analyse(String input, resultCallback result){ + new Thread(new Runnable() { + @Override + public void run() { + List results = textClassifier.classify(input); + + for(int i = 0; i < results.size(); i++){ + Category cat = results.get(i); + switch (cat.getLabel()){ + case "Positive": + result.onFinish(cat.getScore()); + break; + } + } + } + }).start(); + } + + public static float analyse_sync(String input){ + List results = textClassifier.classify(input); + + for(int i = 0; i < results.size(); i++){ + Category cat = results.get(i); + switch (cat.getLabel()){ + case "Positive": + return cat.getScore(); + } + } + return 0; + } +} diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/ui/data/teamsView.java b/app/src/main/java/com/astatin3/scoutingapp2025/ui/data/teamsView.java index 570cc10..8066129 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/ui/data/teamsView.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/ui/data/teamsView.java @@ -13,6 +13,7 @@ import android.widget.TableRow; import android.widget.TextView; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.constraintlayout.widget.ConstraintLayout; import com.astatin3.scoutingapp2025.SettingsVersionStack.latestSettings; @@ -23,10 +24,18 @@ import com.astatin3.scoutingapp2025.types.data.dataType; import com.astatin3.scoutingapp2025.types.frcEvent; import com.astatin3.scoutingapp2025.types.frcTeam; import com.astatin3.scoutingapp2025.types.input.inputType; +import com.astatin3.scoutingapp2025.utility.AlertManager; import com.astatin3.scoutingapp2025.utility.fileEditor; import com.google.android.material.divider.MaterialDivider; +import com.skydoves.powerspinner.IconSpinnerAdapter; +import com.skydoves.powerspinner.IconSpinnerItem; +import com.skydoves.powerspinner.OnSpinnerItemSelectedListener; +import com.skydoves.powerspinner.PowerSpinnerView; +import com.skydoves.powerspinner.SpinnerGravity; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; public class teamsView extends ConstraintLayout { public teamsView(@NonNull Context context) { @@ -115,12 +124,12 @@ public class teamsView extends ConstraintLayout { frcTeam finalTeam = team; tr.setOnClickListener(v -> { - loadTeam(finalTeam, latestSettings.settings.get_compiled_mode()); + loadTeam(finalTeam, latestSettings.settings.get_data_view_mode()); }); } } - public void loadTeam(frcTeam team, boolean compiled_mode) { + public void loadTeam(frcTeam team, int mode) { binding.teamsArea.removeAllViews(); LinearLayout ll = new LinearLayout(getContext()); @@ -131,22 +140,53 @@ public class teamsView extends ConstraintLayout { ll.setOrientation(LinearLayout.VERTICAL); binding.teamsArea.addView(ll); - CheckBox cb = new CheckBox(getContext()); - cb.setLayoutParams(new FrameLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT - )); - cb.setText("Compiled mode"); - cb.setChecked(compiled_mode); - ll.addView(cb); - cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + + + PowerSpinnerView dropdown = new PowerSpinnerView(getContext()); + + List 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(15,15,15,15); + 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() { @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - latestSettings.settings.set_compiled_mode(isChecked); - loadTeam(team, isChecked); + public void onItemSelected(int oldIndex, @Nullable IconSpinnerItem oldItem, int newIndex, + IconSpinnerItem newItem) { + + latestSettings.settings.set_data_view_mode(newIndex); + loadTeam(team, newIndex); } }); + ll.addView(dropdown); + + + + + + + TextView tv = new TextView(getContext()); tv.setLayoutParams(new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, @@ -178,7 +218,7 @@ public class teamsView extends ConstraintLayout { ll.addView(tv); add_pit_data(ll, team); - add_match_data(ll, team, compiled_mode); + add_match_data(ll, team, mode); } public void add_pit_data(LinearLayout ll, frcTeam team){ @@ -248,7 +288,10 @@ public class teamsView extends ConstraintLayout { } } - public void add_match_data(LinearLayout ll, frcTeam team, boolean compiled_mode){ + + + + public void add_match_data(LinearLayout ll, frcTeam team, int mode){ String[] files = fileEditor.getMatchesByTeamNum(evcode, team.teamNumber); ll.addView(new MaterialDivider(getContext())); @@ -266,6 +309,8 @@ public class teamsView extends ConstraintLayout { ll.addView(new MaterialDivider(getContext())); + + if(files.length == 0){ tv = new TextView(getContext()); tv.setLayoutParams(new FrameLayout.LayoutParams( @@ -279,71 +324,121 @@ public class teamsView extends ConstraintLayout { return; } - if(!compiled_mode){ - for (int i = 0; i < files.length; i++) { - String[] split = files[i].split("-"); - int match_num = Integer.parseInt(split[1]); + switch (mode){ + case 0: + add_individual_views(ll,files); + break; + case 1: + add_compiled_views(ll,files); + break; + case 2: + add_history_views(ll,files); + break; + } + } - ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(files[i], match_values, match_transferValues); + + + public void add_individual_views(LinearLayout ll, String[] files) { + for (int i = 0; i < files.length; i++) { + String[] split = files[i].split("-"); + int match_num = Integer.parseInt(split[1]); + + ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(files[i], match_values, match_transferValues); + + 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++) { 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); + tv.setText(psda.data.array[a].name); + tv.setTextSize(25); + + if (psda.data.array[a].isNull()) { + tv.setBackgroundColor(0xffff0000); + tv.setTextColor(0xff000000); + } + ll.addView(tv); - for (int a = 0; a < psda.data.array.length; a++) { - tv = new TextView(getContext()); - tv.setLayoutParams(new FrameLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT - )); - tv.setGravity(Gravity.CENTER_HORIZONTAL); - tv.setText(psda.data.array[a].name); - tv.setTextSize(25); - if(psda.data.array[a].isNull()){ - tv.setBackgroundColor(0xffff0000); - tv.setTextColor(0xff000000); - } - - ll.addView(tv); - - - latest_match_values[a].add_individual_view(ll, psda.data.array[a]); - } - } - - - } else { - dataType[][] data = new dataType[latest_match_values.length][files.length]; - for (int i = 0; i < files.length; i++) { - - ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(files[i], match_values, match_transferValues); - for (int a = 0; a < data.length; a++) { - data[a][i] = psda.data.array[a]; - } - } - - for(int i = 0; i < latest_match_values.length; i++){ - 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(latest_match_values[i].name); - tv.setTextSize(30); - ll.addView(tv); - - latest_match_values[i].add_compiled_view(ll, data[i]); + latest_match_values[a].add_individual_view(ll, psda.data.array[a]); } } } + + + + + + + public void add_compiled_views(LinearLayout ll, String[] files){ + dataType[][] data = new dataType[latest_match_values.length][files.length]; + for (int i = 0; i < files.length; i++) { + + ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(files[i], match_values, match_transferValues); + for (int a = 0; a < data.length; a++) { + data[a][i] = psda.data.array[a]; + } + } + + for(int i = 0; i < latest_match_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(latest_match_values[i].name); + tv.setTextSize(30); + ll.addView(tv); + + latest_match_values[i].add_compiled_view(ll, data[i]); + } + } + + + + + + public void add_history_views(LinearLayout ll, String[] files){ + dataType[][] data = new dataType[latest_match_values.length][files.length]; + for (int i = 0; i < files.length; i++) { + + ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(files[i], match_values, match_transferValues); + for (int a = 0; a < data.length; a++) { + data[a][i] = psda.data.array[a]; + } + } + + for(int i = 0; i < latest_match_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(latest_match_values[i].name); + tv.setTextSize(30); + ll.addView(tv); + + latest_match_values[i].add_history_view(ll, data[i]); + } + } } diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/ui/scouting/scoutingFragment.java b/app/src/main/java/com/astatin3/scoutingapp2025/ui/scouting/scoutingFragment.java index af747eb..8e6089e 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/ui/scouting/scoutingFragment.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/ui/scouting/scoutingFragment.java @@ -11,10 +11,10 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; -import com.astatin3.scoutingapp2025.MainActivity; import com.astatin3.scoutingapp2025.SettingsVersionStack.latestSettings; import com.astatin3.scoutingapp2025.databinding.FragmentScoutingBinding; import com.astatin3.scoutingapp2025.types.frcEvent; +import com.astatin3.scoutingapp2025.ui.data.sentimentAnalysis; import com.astatin3.scoutingapp2025.utility.fileEditor; public class scoutingFragment extends Fragment { @@ -32,7 +32,6 @@ public class scoutingFragment extends Fragment { binding.matchScoutingView.setVisibility(View.GONE); binding.pitScoutingView.setVisibility(View.GONE); - String evcode = latestSettings.settings.get_evcode(); if(evcode.equals("unset")){ diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/TransferFragment.java b/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/TransferFragment.java index 6881d6d..da63fd9 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/TransferFragment.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/TransferFragment.java @@ -56,6 +56,11 @@ public class TransferFragment extends Fragment { evcode = latestSettings.settings.get_evcode(); + binding.downloadButton.setOnClickListener(v -> { + start_download(); + submenu = true; + }); + binding.TBAButton.setOnClickListener(v -> { binding.noEventError.setVisibility(View.GONE); AlertDialog.Builder alert = new AlertDialog.Builder(getContext()); @@ -77,7 +82,7 @@ public class TransferFragment extends Fragment { if(evcode.equals("unset")){ binding.noEventError.setVisibility(View.VISIBLE); binding.uploadButton.setVisibility(View.GONE); - binding.downloadButton.setVisibility(View.GONE); + binding.downloadButton.setVisibility(View.VISIBLE); return root; } @@ -86,11 +91,6 @@ public class TransferFragment extends Fragment { submenu = true; }); - binding.downloadButton.setOnClickListener(v -> { - start_download(); - submenu = true; - }); - if(!latestSettings.settings.get_wifi_mode()) binding.TBAButton.setVisibility(View.GONE); diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/utility/AlertManager.java b/app/src/main/java/com/astatin3/scoutingapp2025/utility/AlertManager.java index 4b77611..e377aa0 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/utility/AlertManager.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/utility/AlertManager.java @@ -10,7 +10,7 @@ import java.io.PrintWriter; import java.io.StringWriter; public class AlertManager { - private static Context context; + public static Context context; public static void init(Context c){ context = c; @@ -53,6 +53,7 @@ public class AlertManager { } public static void error(Exception e) { + e.printStackTrace(); ((Activity) context).runOnUiThread(new Runnable() { public void run() { StringWriter sw = new StringWriter(); diff --git a/app/src/main/res/layout/fragment_data.xml b/app/src/main/res/layout/fragment_data.xml index f4cf29f..b12b112 100644 --- a/app/src/main/res/layout/fragment_data.xml +++ b/app/src/main/res/layout/fragment_data.xml @@ -143,10 +143,12 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginBottom="60dp" + android:visibility="gone" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent"> + app:layout_constraintTop_toTopOf="parent" + tools:visibility="gone">