Fix code scanning sliders and remove asynctask

This commit is contained in:
Michael Mikovsky
2025-07-27 12:57:35 -06:00
parent 4aa31b620d
commit dbba56e649
8 changed files with 246 additions and 144 deletions
@@ -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<frcMatch> 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;
});
}
}
@@ -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<List<Bitmap>> {
// private Function<List<Bitmap>, String> resultFunction = null;
//
// @Override
// protected List<Bitmap> doInBackground(String... strings) {
//
//
// return new ArrayList<>();
// }
//
//
// public void onResult(Function<List<Bitmap>, String> func) {
// this.resultFunction = func;
// }
//
//
// @Override
// protected void onPostExecute(List<Bitmap> 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<Bitmap> call() {
List<Bitmap> 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<EncodeHintType, Object> 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;
}
}
@@ -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<Bitmap> qrBitmaps;
private List<Bitmap> 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<EncodeHintType, Object> 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);
}
}
@@ -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;
@@ -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;
});
}
}
@@ -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;
}
});
@@ -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<R> {
void onComplete(R result);
}
public <R> void executeAsync(Callable<R> callable, Callback<R> callback) {
executor.execute(() -> {
final R result;
try {
result = callable.call();
handler.post(() -> {
callback.onComplete(result);
});
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
}