diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/Utils/frcEvent.java b/app/src/main/java/com/astatin3/scoutingapp2025/Utils/frcEvent.java new file mode 100644 index 0000000..6b0cc03 --- /dev/null +++ b/app/src/main/java/com/astatin3/scoutingapp2025/Utils/frcEvent.java @@ -0,0 +1,10 @@ +package com.astatin3.scoutingapp2025.Utils; + +import java.util.ArrayList; + +public class frcEvent { + public String eventCode; + public String name; + public ArrayList matches; + public ArrayList teams; +} diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/Utils/frcMatch.java b/app/src/main/java/com/astatin3/scoutingapp2025/Utils/frcMatch.java new file mode 100644 index 0000000..15414a7 --- /dev/null +++ b/app/src/main/java/com/astatin3/scoutingapp2025/Utils/frcMatch.java @@ -0,0 +1,8 @@ +package com.astatin3.scoutingapp2025.Utils; + +public class frcMatch { + public frcMatch(){} + public int matchIndex = 0; + public int[] blueAlliance; + public int[] redAlliance; +} \ No newline at end of file diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/Utils/frcTeam.java b/app/src/main/java/com/astatin3/scoutingapp2025/Utils/frcTeam.java new file mode 100644 index 0000000..d597186 --- /dev/null +++ b/app/src/main/java/com/astatin3/scoutingapp2025/Utils/frcTeam.java @@ -0,0 +1,22 @@ +package com.astatin3.scoutingapp2025.Utils; + +public class frcTeam { + public int teamNumber = 0; + public String teamName = null; + public String city = null; + public String stateOrProv = null; + public String school = null; + public String country = null; + public int startingYear = 0; + + public String getDescription(){ + return teamName + " Started in " + startingYear + ", and is from " + school + " in " + city + ", " + stateOrProv + ", " + country; + } + + public frcTeam encode(){ + return this; + } + public frcTeam decode(){ + return this; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/fileEditor.java b/app/src/main/java/com/astatin3/scoutingapp2025/fileEditor.java new file mode 100644 index 0000000..d2eb44d --- /dev/null +++ b/app/src/main/java/com/astatin3/scoutingapp2025/fileEditor.java @@ -0,0 +1,128 @@ +package com.astatin3.scoutingapp2025; + +import android.content.Context; + +import com.astatin3.scoutingapp2025.Utils.frcMatch; +import com.astatin3.scoutingapp2025.Utils.frcTeam; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.nio.BufferOverflowException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.zip.DataFormatException; +import java.util.zip.Deflater; +import java.util.zip.Inflater; + +public final class fileEditor { +// private final static String baseDir = android.os.Environment.getExternalStorageDirectory().getAbsolutePath(); + public static final byte internalDataVersion = 0x01; + + public static String binaryVisualize(byte[] bytes){ + String returnStr = ""; + for(int a=0;a> b) & 1); + } + returnStr += " (" + (int)bytes[a] + ")\n"; + } + return returnStr; + } + + public static char toChar(int num){ + if(num < 0 || num > 255){ + throw new BufferOverflowException(); + } + byte[] bytes = new byte[1]; + bytes[0] = (byte) num; + return new String(bytes, Charset.defaultCharset()).charAt(0); + } + + public static int fromChar(char c){ + byte[] bytes = (String.valueOf(c)).getBytes(Charset.defaultCharset()); + return Byte.toUnsignedInt(bytes[0]); + } + + public static byte[] compress(byte[] input) { + Deflater deflater = new Deflater(); + deflater.setInput(input); + deflater.finish(); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + + while (!deflater.finished()) { + int compressedSize = deflater.deflate(buffer); + outputStream.write(buffer, 0, compressedSize); + } + + return outputStream.toByteArray(); + } + + + public static byte[] decompress(byte[] input) throws DataFormatException { + Inflater inflater = new Inflater(); + inflater.setInput(input); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + + while (!inflater.finished()) { + int decompressedSize = inflater.inflate(buffer); + outputStream.write(buffer, 0, decompressedSize); + } + + return outputStream.toByteArray(); + } + + private static boolean writeToFile(Context context, String filepath, String data) { + try { + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(context.openFileOutput(filepath, Context.MODE_PRIVATE)); + outputStreamWriter.write(data); + outputStreamWriter.close(); + return true; + } + catch (IOException e) { + e.printStackTrace(); + return false; + } + } + + private static String intSplit(int[] intArr, String splitStr){ + String returnStr = ""; + for(int i=0;i matches){ + final String filename = (key + "-matches.csv"); + + String csvData = ""; + + csvData += key + "\n"; + csvData += matchName + "\n"; + + for(int i=0;i teams){ + return true; + } +} + diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/TBAView.java b/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/TBAView.java index e00f01b..d00f14c 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/TBAView.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/TBAView.java @@ -2,12 +2,8 @@ package com.astatin3.scoutingapp2025.ui.transfer; import android.app.AlertDialog; import android.content.Context; -import android.content.DialogInterface; -import android.graphics.drawable.Drawable; -import android.os.Bundle; import android.util.AttributeSet; import android.view.Gravity; -import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; @@ -16,31 +12,26 @@ import android.widget.TableLayout; import android.widget.TableRow; import android.widget.TextView; -import androidx.annotation.NonNull; -import androidx.constraintlayout.widget.ConstraintLayout; -import androidx.constraintlayout.widget.ConstraintSet; - -import com.astatin3.scoutingapp2025.R; import com.astatin3.scoutingapp2025.RequestTask; -import com.astatin3.scoutingapp2025.databinding.FragmentTbaBinding; +import com.astatin3.scoutingapp2025.Utils.frcMatch; +import com.astatin3.scoutingapp2025.Utils.frcTeam; import com.astatin3.scoutingapp2025.databinding.FragmentTransferBinding; +import com.astatin3.scoutingapp2025.fileEditor; import com.astatin3.scoutingapp2025.ui.JSONUtil; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -import org.w3c.dom.Text; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Calendar; import java.util.Comparator; import java.util.Date; import java.util.function.Function; public class TBAView extends ScrollView { - - // private final String private final String TBAAddress = "https://www.thebluealliance.com/api/v3/"; private final String TBAHeader = "X-TBA-Auth-Key: tjEKSZojAU2pgbs2mBt06SKyOakVhLutj3NwuxLTxPKQPLih11aCIwRIVFXKzY4e"; @@ -68,6 +59,12 @@ public class TBAView extends ScrollView { Table = binding.matchTable; + Table.setStretchAllColumns(true); + + TableRow tr = new TableRow(getContext()); + addTableText(tr, "Loading Events..."); + Table.addView(tr); + final RequestTask rq = new RequestTask(); rq.onResult(new Function() { @Override @@ -89,6 +86,10 @@ public class TBAView extends ScrollView { } public void eventTable(String dataString){ + Table.removeAllViews(); + Table.setStretchAllColumns(true); + Table.bringToFront(); + Date currentTime = Calendar.getInstance().getTime(); try { @@ -158,15 +159,35 @@ public class TBAView extends ScrollView { tr.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { + Table.removeAllViews(); + Table.setStretchAllColumns(true); + Table.bringToFront(); + + TableRow tr = new TableRow(getContext()); + addTableText(tr, "Downloading Teams..."); + Table.addView(tr); + final RequestTask rq = new RequestTask(); rq.onResult(new Function() { @Override - public String apply(String s) { - matchTable(s); + public String apply(String teamsStr) { + TableRow tr = new TableRow(getContext()); + addTableText(tr, "Downloading Matches..."); + Table.addView(tr); + + final RequestTask rq = new RequestTask(); + rq.onResult(new Function() { + @Override + public String apply(String matchesStr) { + matchTable(matchesStr, teamsStr, j); + return null; + } + }); + rq.execute((TBAAddress + "event/" + matchKey + "/matches"), TBAHeader); return null; } }); - rq.execute((TBAAddress + "event/" + matchKey + "/matches"), TBAHeader); + rq.execute((TBAAddress + "event/" + matchKey + "/teams"), TBAHeader); } }); @@ -194,17 +215,46 @@ public class TBAView extends ScrollView { } - public void matchTable(String dataString){ + + public void matchTable(String matchesString, String teamsString, JSONObject eventData){ + Table.removeAllViews(); + Table.setStretchAllColumns(true); + Table.bringToFront(); + try { - JSONArray data = new JSONArray(dataString); + final JSONArray matchData = new JSONArray(matchesString); + final JSONArray teamData = new JSONArray(teamsString); - Table.removeAllViews(); - Table.setStretchAllColumns(true); - Table.bringToFront(); + String matchKey = eventData.getString("key"); + String matchName = eventData.getString("short_name"); + // Sometimes, a short name is not present on TBA Events + if(matchName.isEmpty()){ + matchName = eventData.getString("name"); + } + // Event code at top + TextView tv = new TextView(getContext()); + tv.setLayoutParams(new LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + )); + tv.setText(matchKey); + tv.setTextSize(18); + Table.addView(tv); - if(data.length() == 0){ + // Event Name + tv = new TextView(getContext()); + tv.setLayoutParams(new LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + )); + tv.setGravity(Gravity.CENTER_HORIZONTAL); + tv.setText(matchName); + tv.setTextSize(28); + Table.addView(tv); + + if(matchData.length() == 0){ TableRow tr = new TableRow(getContext()); addTableText(tr, "This event has no matches released yet..."); Table.addView(tr); @@ -214,7 +264,7 @@ public class TBAView extends ScrollView { return; } - TableRow tr = new TableRow(getContext()); + // Save button Button btn = new Button(getContext()); btn.setText("Save"); btn.setTextSize(18); @@ -222,11 +272,10 @@ public class TBAView extends ScrollView { ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT )); -// addTableText(tr, "test"); - tr.addView(btn); - Table.addView(tr); + Table.addView(btn); - tr = new TableRow(getContext()); + + TableRow tr = new TableRow(getContext()); addTableText(tr, "#"); addTableText(tr, "Red-1"); addTableText(tr, "Red-2"); @@ -237,7 +286,7 @@ public class TBAView extends ScrollView { Table.addView(tr); - data = JSONUtil.sort(data, new Comparator(){ + final JSONArray sortedMatchData = JSONUtil.sort(matchData, new Comparator(){ public int compare(Object a, Object b){ JSONObject ja = (JSONObject)a; JSONObject jb = (JSONObject)b; @@ -250,11 +299,12 @@ public class TBAView extends ScrollView { }); + final ArrayList matchesOBJ = new ArrayList(); boolean toggle = false; int matchCount = 1; - for(int a=0;a matchData, JSONArray teamData, JSONObject eventData){ + try { + final String matchKey = eventData.getString("key"); + String matchName = eventData.getString("short_name"); + + // Sometimes, a short name is not present on TBA Events + if(matchName.isEmpty()){ + matchName = eventData.getString("name"); + } + + + ArrayList teams = new ArrayList(); + for(int i=0;i qrBitmaps; + public generatorView(Context context) { super(context); } @@ -90,17 +98,44 @@ public class generatorView extends ConstraintLayout { return bitmap; } - public void start(FragmentTransferBinding binding, String data){ + public void start(FragmentTransferBinding binding, String inputData){ qrImage = binding.qrImage; qrSpeedSlider = binding.qrSpeedSlider; qrSizeSlider = binding.qrSizeSlider; qrIndexN = binding.qrIndexN; qrIndexD = binding.qrIndexD; - sendData(data); + String compiledData = null; + try { + byte[] tempData = fileEditor.compress(inputData.getBytes("UTF-8")); + compiledData = new String(tempData, "UTF-8"); + alert(""+tempData.length, fileEditor.binaryVisualize(tempData)); + Log.i("Info", fileEditor.binaryVisualize(tempData)); + + }catch (UnsupportedEncodingException e){ + e.printStackTrace(); + } + + + if(compiledData == null || inputData.length() < compiledData.length()){ + sendData(inputData); + }else{ + sendData(compiledData); + } + + } + + private void alert(String title, String content) { + AlertDialog.Builder alert = new AlertDialog.Builder(getContext()); + alert.setMessage(content); + alert.setTitle(title); + alert.setPositiveButton("OK", null); + alert.setCancelable(true); + alert.create().show(); } private void sendData(String data){ + minQrSize = Math.round(data.length()/maxQrCount); qrSizeSlider.setMax(maxQrSize-minQrSize); @@ -139,15 +174,19 @@ public class generatorView extends ConstraintLayout { qrSpeedSlider.setProgress(defaultQrDelay+5); qrBitmaps = new ArrayList(); - for(int i=0;i<=(data.length()/qrSize);i++){ + for(int i=0;i<=((data.length()+1)/qrSize);i++){ final int start = i*qrSize; int end = (i+1)*qrSize; - if(end > data.length()){ - end = data.length()-1; + if(end >= data.length()){ + end = data.length(); } try { +// alert("test", ""+Math.ceil((double)data.length()/(double)qrSize)); qrBitmaps.add(generateQrCode( - data.substring(start, end) + String.valueOf(fileEditor.toChar(fileEditor.internalDataVersion)) + + String.valueOf(fileEditor.toChar(i)) + + String.valueOf(fileEditor.toChar(qrCount-1)) + + data.substring(start, end) )); }catch (WriterException e){ e.printStackTrace(); @@ -173,6 +212,7 @@ public class generatorView extends ConstraintLayout { this.qrIndex = this.qrCount-1; } } + qrIndexN.setText(String.valueOf(qrIndex+1)); } diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/scannerView.java b/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/scannerView.java index c8295ff..e743a01 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/scannerView.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/scannerView.java @@ -1,28 +1,60 @@ package com.astatin3.scoutingapp2025.ui.transfer; import android.app.ActionBar; +import android.app.AlertDialog; import android.content.Context; import android.graphics.PointF; import android.util.AttributeSet; +import android.util.Log; +import android.view.SurfaceView; import android.view.View; import androidx.constraintlayout.widget.ConstraintLayout; import com.astatin3.scoutingapp2025.databinding.FragmentTransferBinding; +import com.astatin3.scoutingapp2025.fileEditor; import com.astatin3.scoutingapp2025.qrPointsOverlayView; import com.dlazaro66.qrcodereaderview.QRCodeReaderView; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.zip.DataFormatException; + public class scannerView extends ConstraintLayout { + public static class fixedQRCodeReaderView extends QRCodeReaderView { + public fixedQRCodeReaderView(Context context) { + super(context, null); + } + } + private QRCodeReaderView qrCodeReaderView; private qrPointsOverlayView pointsOverlayView; + private String[] qrDataArr; private class codeReadListener implements QRCodeReaderView.OnQRCodeReadListener { @Override public void onQRCodeRead(String text, PointF[] points) { pointsOverlayView.setPoints(points); + + compileData( + fileEditor.fromChar(text.charAt(0)), + fileEditor.fromChar(text.charAt(1)), + (fileEditor.fromChar(text.charAt(2))+1), + text.substring(3) + ); } } + private void alert(String title, String content) { + AlertDialog.Builder alert = new AlertDialog.Builder(getContext()); + alert.setMessage(content); + alert.setTitle(title); + alert.setPositiveButton("OK", null); + alert.setCancelable(true); + alert.create().show(); + } + public scannerView(Context context) { super(context); } @@ -63,4 +95,46 @@ public class scannerView extends ConstraintLayout { } }); } + + private void compileData(int dataVersion, int qrIndex, int qrCount, String qrData){ + if(dataVersion != fileEditor.internalDataVersion){ + alert("Error", "Incorrect data version"); + return; + } + if(qrDataArr == null || qrDataArr.length != qrCount){ + qrDataArr = new String[qrCount]; + } + + if(qrDataArr[qrIndex] == null) { + qrDataArr[qrIndex] = qrData; + alert((qrIndex+1)+"/"+qrCount, qrData); + } + + int count = 0; + for(int i =0;i= qrCount){ + + // I guess String.join does not like non-ascii text + String compiledData = ""; + for(int i=0;i