From dbba56e649ba4278d077bcdca44a33c47337ed17 Mon Sep 17 00:00:00 2001 From: Michael Mikovsky <77305074+Astatin3@users.noreply.github.com> Date: Sun, 27 Jul 2025 12:57:35 -0600 Subject: [PATCH] Fix code scanning sliders and remove asynctask --- README.md | 19 +-- .../ui/transfer/TBAEventFragment.java | 30 +--- .../ui/transfer/codes/CodeGenTask.java | 129 +++++++++++++++ .../ui/transfer/codes/CodeGeneratorView.java | 154 +++++++----------- .../ui/transfer/codes/CodeOverlayView.java | 2 +- .../ridgescout/utility/AlertManager.java | 19 +++ .../ridgescout/utility/FileEditor.java | 5 +- .../ridgescout/utility/TaskRunner.java | 32 ++++ 8 files changed, 246 insertions(+), 144 deletions(-) create mode 100644 app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/codes/CodeGenTask.java create mode 100644 app/src/main/java/com/ridgebotics/ridgescout/utility/TaskRunner.java diff --git a/README.md b/README.md index 8476615..990d875 100644 --- a/README.md +++ b/README.md @@ -33,21 +33,4 @@ https://www.thebluealliance.com/avatars |Match scouting interface|Field editor|Teams data viewer| |-|-|-| |![Screenshot1](https://github.com/Team4388/ScoutingApp2025/blob/main/metadata/en-US/images/phoneScreenshots/1.png?raw=true)|![Screenshot2](https://github.com/Team4388/ScoutingApp2025/blob/main/metadata/en-US/images/phoneScreenshots/2.png?raw=true)|![Screenshot3](https://github.com/Team4388/ScoutingApp2025/blob/main/metadata/en-US/images/phoneScreenshots/3.png?raw=true)| - - - \ No newline at end of file +z \ No newline at end of file diff --git a/app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/TBAEventFragment.java b/app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/TBAEventFragment.java index 3ab7c66..4c20a5f 100644 --- a/app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/TBAEventFragment.java +++ b/app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/TBAEventFragment.java @@ -49,8 +49,6 @@ public class TBAEventFragment extends Fragment { private final int year = SettingsManager.getYearNum(); - private ProgressDialog loadingDialog; - private static JSONObject eventData = null; public static void setEventData(JSONObject j){ eventData = j; @@ -74,7 +72,7 @@ public class TBAEventFragment extends Fragment { Table.setStretchAllColumns(true); - startLoading("Loading Teams and Matches..."); + AlertManager.startLoading("Loading Teams and Matches..."); Table.removeAllViews(); Table.setStretchAllColumns(true); Table.bringToFront(); @@ -92,7 +90,7 @@ public class TBAEventFragment extends Fragment { final RequestTask rq1 = new RequestTask(); rq1.onResult(matchesStr -> { matchTable(matchesStr, teamsStr, eventData); - stopLoading(); + AlertManager.stopLoading(); return null; }); rq1.execute((TBAAddress + "event/" + matchKey + "/matches"), TBAHeader); @@ -375,12 +373,12 @@ public class TBAEventFragment extends Fragment { }catch (JSONException j){ AlertManager.error("Failed Downloading", j); - stopLoading(); + AlertManager.stopLoading(); } } private boolean saveData(ArrayList matchData, JSONArray teamData, JSONObject eventData){ - startLoading("Saving data..."); + AlertManager.startLoading("Saving data..."); Thread t = new Thread(() -> { try { @@ -431,31 +429,15 @@ public class TBAEventFragment extends Fragment { AlertManager.toast("Saved!"); getActivity().runOnUiThread(() -> findNavController(this).navigate(R.id.action_navigation_tba_event_to_navigation_transfer)); - stopLoading(); + AlertManager.stopLoading(); }catch(Exception j) { AlertManager.error(j); - stopLoading(); + AlertManager.stopLoading(); } }); t.start(); return false; } - - private void startLoading(String title){ - getActivity().runOnUiThread(() -> { - if(loadingDialog != null && loadingDialog.isShowing()) - loadingDialog.dismiss(); - loadingDialog = ProgressDialog.show(getActivity(), title, "Please wait..."); - }); - } - - private void stopLoading(){ - getActivity().runOnUiThread(() -> { - if (loadingDialog != null) - loadingDialog.cancel(); - loadingDialog = null; - }); - } } diff --git a/app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/codes/CodeGenTask.java b/app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/codes/CodeGenTask.java new file mode 100644 index 0000000..7a0f488 --- /dev/null +++ b/app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/codes/CodeGenTask.java @@ -0,0 +1,129 @@ +package com.ridgebotics.ridgescout.ui.transfer.codes; + +import android.graphics.Bitmap; +import android.graphics.Color; +import android.os.AsyncTask; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.EncodeHintType; +import com.google.zxing.MultiFormatWriter; +import com.google.zxing.WriterException; +import com.google.zxing.common.BitMatrix; +import com.ridgebotics.ridgescout.utility.AlertManager; +import com.ridgebotics.ridgescout.utility.FileEditor; +import com.ridgebotics.ridgescout.utility.TaskRunner; + +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.function.Function; + +public class CodeGenTask implements Callable> { +// private Function, String> resultFunction = null; +// +// @Override +// protected List doInBackground(String... strings) { +// + +// +// return new ArrayList<>(); +// } +// +// +// public void onResult(Function, String> func) { +// this.resultFunction = func; +// } +// +// +// @Override +// protected void onPostExecute(List result) { +// super.onPostExecute(result); +// if(resultFunction != null){ +// resultFunction.apply(result); +// } +// } + + private final String data; + private final int randID; + private final int qrSize; + private final int qrCount; + + public CodeGenTask(String data, int randID, int qrSize, int qrCount) { + this.data = data; + this.randID = randID; + this.qrSize = qrSize; + this.qrCount = qrCount; + } + + @Override + public List call() { + List qrBitmaps = new ArrayList<>(); + + for(int i=0;i<=((data.length()+1)/qrSize);i++){ + final int start = i*qrSize; + int end = (i+1)*qrSize; + if(end >= data.length()){ + end = data.length(); + } + try { +// alert("test", ""+Math.ceil((double)data.length()/(double)qrSize)); + qrBitmaps.add(generateQrCode( + FileEditor.byteToChar(FileEditor.internalDataVersion, FileEditor.lengthHeaderBytes) + + String.valueOf(FileEditor.byteToChar(randID, FileEditor.lengthHeaderBytes)) + + FileEditor.byteToChar(i, FileEditor.lengthHeaderBytes) + + FileEditor.byteToChar(qrCount - 1, FileEditor.lengthHeaderBytes) + + data.substring(start, end) + )); +// alert("title", ""+(qrCount-1)); + }catch (WriterException e){ + AlertManager.error(e); + } + } + + return qrBitmaps; + } + + private Bitmap generateQrCode(String contents) throws WriterException { + + final int size = 512; + + if (contents == null) { + return null; + } + + Map hints = new EnumMap<>(EncodeHintType.class); + + // The Charset must be UTF-8, Or data will not be transferred properly. IDK why. + hints.put(EncodeHintType.CHARACTER_SET, "ISO-8859-1"); +// hints.put(EncodeHintType.); + hints.put(EncodeHintType.MARGIN, 0); /* default = 4 */ + MultiFormatWriter writer = new MultiFormatWriter(); + + BitMatrix result; + try { + result = writer.encode(contents, BarcodeFormat.DATA_MATRIX, size, size, hints); + } catch (IllegalArgumentException e) { + // Unsupported format + AlertManager.error(e); + return null; + } + + int width = result.getWidth(); + int height = result.getHeight(); + int[] pixels = new int[width * height]; + for (int y = 0; y < height; y++) { + int offset = y * width; + for (int x = 0; x < width; x++) { + pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.WHITE; + } + } + + Bitmap bitmap = Bitmap.createBitmap(width, height, + Bitmap.Config.ARGB_8888); + bitmap.setPixels(pixels, 0, width, 0, 0, width, height); + + return bitmap; + } +} diff --git a/app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/codes/CodeGeneratorView.java b/app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/codes/CodeGeneratorView.java index 35d9302..0b2288c 100644 --- a/app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/codes/CodeGeneratorView.java +++ b/app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/codes/CodeGeneratorView.java @@ -4,6 +4,7 @@ import android.graphics.Bitmap; import android.graphics.Color; import android.os.Bundle; import android.os.CountDownTimer; +import android.os.Handler; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -23,10 +24,12 @@ import com.google.zxing.EncodeHintType; import com.google.zxing.MultiFormatWriter; import com.google.zxing.WriterException; import com.google.zxing.common.BitMatrix; +import com.ridgebotics.ridgescout.utility.TaskRunner; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.EnumMap; +import java.util.List; import java.util.Map; import java.util.Random; @@ -38,23 +41,27 @@ public class CodeGeneratorView extends Fragment { private TextView qrIndexN; private TextView qrIndexD; - private final int maxQrCount = 256; //The max number that can be stored in a byte + private static final int maxQrCount = 256; //The max number that can be stored in a byte + + private static final int maxQrSize = 800; + + + private static final int maxQrSpeed = 50; + private static final int minQrSpeed = 1000; + private static final int defaultQrDelay = 12; + - private final int maxQrSpeed = 5; - private final int minQrSpeed = 300 + maxQrSpeed - 1; private int minQrSize = 0; - private final int maxQrSize = 800; private int qrSize = 200; - private final int defaultQrDelay = 419; - private int qrDelay = 0; + private double qrDelay = 0; private int qrIndex = 0; private CountDownTimer timer; private int qrCount = 0; - private ArrayList qrBitmaps; + private List qrBitmaps = new ArrayList<>(); private FragmentTransferCodeSenderBinding binding; @@ -85,75 +92,29 @@ public class CodeGeneratorView extends Fragment { } minQrSize = Math.round((float)compressed.length() / maxQrCount)+1; - - qrSizeSlider.setMax(maxQrSize-minQrSize); - qrSpeedSlider.setMax((minQrSpeed-maxQrSpeed)*2); - - qrSizeSlider.setProgress(minQrSize+qrSize); - qrSpeedSlider.setProgress(defaultQrDelay+5); + qrSize += minQrSize; sendData(compressed); + qrSpeedSlider.setMax(maxQrSpeed*2); + qrSpeedSlider.setProgress(maxQrSpeed + defaultQrDelay); + + qrSizeSlider.setMax(maxQrSize-minQrSize); + qrSizeSlider.setProgress(qrSize-minQrSize); + + startLoop(); + return binding.getRoot(); } - - private Bitmap generateQrCode(String contents) throws WriterException { - - final int size = 512; - - if (contents == null) { - return null; - } - - Map hints = new EnumMap<>(EncodeHintType.class); - - // The Charset must be UTF-8, Or data will not be transferred properly. IDK why. - hints.put(EncodeHintType.CHARACTER_SET, "ISO-8859-1"); -// hints.put(EncodeHintType.); - hints.put(EncodeHintType.MARGIN, 0); /* default = 4 */ - MultiFormatWriter writer = new MultiFormatWriter(); - - BitMatrix result; - try { - result = writer.encode(contents, BarcodeFormat.DATA_MATRIX, size, size, hints); - } catch (IllegalArgumentException e) { - // Unsupported format - AlertManager.error(e); - return null; - } - - int width = result.getWidth(); - int height = result.getHeight(); - int[] pixels = new int[width * height]; - for (int y = 0; y < height; y++) { - int offset = y * width; - for (int x = 0; x < width; x++) { - pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.WHITE; - } - } - - Bitmap bitmap = Bitmap.createBitmap(width, height, - Bitmap.Config.ARGB_8888); - bitmap.setPixels(pixels, 0, width, 0, 0, width, height); - - return bitmap; - } - - private void sendData(String data){ - - - qrCount = (data.length()/qrSize)+1; qrIndexD.setText(String.valueOf(qrCount)); -// alert("size", ""+binding.qrSizeSlider.getProgress()+"\n"+binding.qrSizeSlider.getMax()); - qrSpeedSlider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - qrDelay = -(minQrSpeed - progress - maxQrSpeed + 1); + qrDelay = ((double) progress /maxQrSpeed) - 1; } @Override public void onStartTrackingTouch(SeekBar seekBar) {} @@ -176,40 +137,20 @@ public class CodeGeneratorView extends Fragment { } }); -// qrSizeSlider.setProgress(qr); + AlertManager.startLoading("Generating codes..."); - qrBitmaps = new ArrayList<>(); + new TaskRunner().executeAsync(new CodeGenTask(data, new Random().nextInt(255), qrSize, qrCount), result -> { + qrBitmaps = result; + AlertManager.stopLoading(); + qrIndex = 0; + }); - int randID = new Random().nextInt(255); - - for(int i=0;i<=((data.length()+1)/qrSize);i++){ - final int start = i*qrSize; - int end = (i+1)*qrSize; - if(end >= data.length()){ - end = data.length(); - } - try { -// alert("test", ""+Math.ceil((double)data.length()/(double)qrSize)); - qrBitmaps.add(generateQrCode( - FileEditor.byteToChar(FileEditor.internalDataVersion, FileEditor.lengthHeaderBytes) + - String.valueOf(FileEditor.byteToChar(randID, FileEditor.lengthHeaderBytes)) + - FileEditor.byteToChar(i, FileEditor.lengthHeaderBytes) + - FileEditor.byteToChar(qrCount - 1, FileEditor.lengthHeaderBytes) + - data.substring(start, end) - )); -// alert("title", ""+(qrCount-1)); - }catch (WriterException e){ - AlertManager.error(e); - } - } - qrIndex = 0; - if(timer != null){ - timer.cancel(); - } - qrLoop(); } private void updateQr(){ + if(qrBitmaps.isEmpty()) + return; + qrImage.setImageBitmap(qrBitmaps.get(qrIndex)); if(qrDelay > 0) { this.qrIndex += 1; @@ -226,13 +167,28 @@ public class CodeGeneratorView extends Fragment { qrIndexN.setText(String.valueOf(qrIndex+1)); } - private void qrLoop(){ - timer = new CountDownTimer(minQrSpeed-Math.abs(qrDelay)+1, 1000) { - public void onTick(long millisUntilFinished) {} - public void onFinish() { - updateQr(); - qrLoop(); + private void startLoop() { + + + final Handler handler = new Handler(); + Runnable runnable = new Runnable() { + + @Override + public void run() { + try{ + updateQr(); + } + catch (Exception e) { + AlertManager.error(e); + } + finally{ + double a = ((double) maxQrSpeed) / (Math.abs(qrDelay)); + a = Math.min(Math.max(a, maxQrSpeed), minQrSpeed); + handler.postDelayed(this, (long) a); + } } - }.start(); + }; + + handler.post(runnable); } } diff --git a/app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/codes/CodeOverlayView.java b/app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/codes/CodeOverlayView.java index ee3c2ea..7948db3 100644 --- a/app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/codes/CodeOverlayView.java +++ b/app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/codes/CodeOverlayView.java @@ -65,7 +65,7 @@ public class CodeOverlayView extends View { } } if(barColors != null){ - final double width = getWidth()/barColors.length; + final double width = (double) getWidth() /barColors.length; final int top = 0; final int bottom = barHeight; diff --git a/app/src/main/java/com/ridgebotics/ridgescout/utility/AlertManager.java b/app/src/main/java/com/ridgebotics/ridgescout/utility/AlertManager.java index 463a295..8094609 100644 --- a/app/src/main/java/com/ridgebotics/ridgescout/utility/AlertManager.java +++ b/app/src/main/java/com/ridgebotics/ridgescout/utility/AlertManager.java @@ -3,6 +3,7 @@ package com.ridgebotics.ridgescout.utility; import android.annotation.SuppressLint; import android.app.Activity; import android.app.AlertDialog; +import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.widget.Toast; @@ -113,4 +114,22 @@ public class AlertManager { }); } + private static ProgressDialog loadingDialog; + + public static void startLoading(String title){ + ((Activity) context).runOnUiThread(() -> { + if(loadingDialog != null && loadingDialog.isShowing()) + loadingDialog.dismiss(); + loadingDialog = ProgressDialog.show(context, title, "Please wait..."); + }); + } + + public static void stopLoading(){ + ((Activity) context).runOnUiThread(() -> { + if (loadingDialog != null) + loadingDialog.cancel(); + loadingDialog = null; + }); + } + } diff --git a/app/src/main/java/com/ridgebotics/ridgescout/utility/FileEditor.java b/app/src/main/java/com/ridgebotics/ridgescout/utility/FileEditor.java index b57d3bb..e42f959 100644 --- a/app/src/main/java/com/ridgebotics/ridgescout/utility/FileEditor.java +++ b/app/src/main/java/com/ridgebotics/ridgescout/utility/FileEditor.java @@ -398,9 +398,10 @@ public final class FileEditor { Arrays.sort(filenames, (o1, o2) -> { try { if (!o1.contains("-") || !o2.contains("-")) - return 0; - return Integer.valueOf(o1.split("-")[1]).compareTo(Integer.valueOf(o2.split("-")[1])); + return o2.compareTo(o1); + return Integer.valueOf(o1.split("-")[1].split("\\.")[0]).compareTo(Integer.valueOf(o2.split("-")[1].split("\\.")[0])); } catch (Exception e) { + AlertManager.error(e); return 0; } }); diff --git a/app/src/main/java/com/ridgebotics/ridgescout/utility/TaskRunner.java b/app/src/main/java/com/ridgebotics/ridgescout/utility/TaskRunner.java new file mode 100644 index 0000000..9e95a38 --- /dev/null +++ b/app/src/main/java/com/ridgebotics/ridgescout/utility/TaskRunner.java @@ -0,0 +1,32 @@ +package com.ridgebotics.ridgescout.utility; + +import android.os.Handler; +import android.os.Looper; + +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +// https://stackoverflow.com/questions/58767733/the-asynctask-api-is-deprecated-in-android-11-what-are-the-alternatives +public class TaskRunner { + private final Executor executor = Executors.newSingleThreadExecutor(); // change according to your requirements + private final Handler handler = new Handler(Looper.getMainLooper()); + + public interface Callback { + void onComplete(R result); + } + + public void executeAsync(Callable callable, Callback callback) { + executor.execute(() -> { + final R result; + try { + result = callable.call(); + handler.post(() -> { + callback.onComplete(result); + }); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } +} \ No newline at end of file