Make scanner better, format layout better.

This commit is contained in:
Astatin3
2024-03-31 15:13:53 -06:00
parent 9ef0b75c2c
commit b9273bd4f8
17 changed files with 844 additions and 574 deletions
@@ -1,6 +1,13 @@
package com.astatin3.scoutingapp2025;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.os.AsyncTask;
import android.util.AttributeSet;
import android.view.View;
import java.io.BufferedReader;
import java.io.IOException;
@@ -0,0 +1,53 @@
package com.astatin3.scoutingapp2025;
// From https://github.com/dlazaro66/QRCodeReaderView/blob/master/samples/src/main/java/com/example/qr_readerexample/PointsOverlayView.java
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.View;
public class qrPointsOverlayView extends View {
PointF[] points;
private Paint paint;
public qrPointsOverlayView(Context context) {
super(context);
init();
}
public qrPointsOverlayView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public qrPointsOverlayView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
paint = new Paint();
paint.setColor(Color.YELLOW);
paint.setStyle(Paint.Style.FILL);
}
public void setPoints(PointF[] points) {
this.points = points;
invalidate();
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
if (points != null) {
for (PointF pointF : points) {
canvas.drawCircle(pointF.x, pointF.y, 10, paint);
}
}
}
}
@@ -44,239 +44,18 @@ public class TBAFragment extends Fragment {
alert.create().show();
}
// abstract class TBATask extends RequestTask {
// public static void run(String... uri){
// super.execute(uri);
// }
// private void response(String result){
// alert("data", result);
// }
// }
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
// TBAViewModel dashboardViewModel =
// new ViewModelProvider(this).get(TBAViewModel.class);
binding = FragmentTbaBinding.inflate(inflater, container, false);
View root = binding.getRoot();
ScrollArea = binding.ScrollArea;
Table = binding.matchTable;
warnPopup();
return root;
}
public void warnPopup() {
// alert("App Title", "This is an alert with no consequence");
//
AlertDialog.Builder alert = new AlertDialog.Builder(getContext());
alert.setMessage("This is an alert with no consequence");
alert.setTitle("App Title");
alert.setCancelable(true);
alert.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
final RequestTask rq = new RequestTask();
rq.onResult(new Function<String, String>() {
@Override
public String apply(String s) {
eventTable(s);
return null;
}
});
rq.execute(TBAAddress + "events/2024", TBAHeader);
}
});
alert.create().show();
}
private void addTableText(TableRow tr, String textStr){
TextView text = new TextView(getContext());
text.setTextSize(18);
text.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); // Text align center
text.setText(textStr);
tr.addView(text);
}
public void eventTable(String dataString){
try {
JSONArray data = new JSONArray(dataString);
Table.setStretchAllColumns(true);
Table.bringToFront();
boolean toggle = false;
TableRow tr = new TableRow(getContext());
addTableText(tr, "Key");
addTableText(tr, "Title");
addTableText(tr, "Type");
Table.addView(tr);
for(int i=0;i<data.length();i++){
tr = new TableRow(getContext());
if (toggle) {
tr.setBackgroundColor(0x30000000);
}
JSONObject j = data.getJSONObject(i);
Button button = new Button(getContext());
String matchKey = j.getString("key");
button.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View v) {
final RequestTask rq = new RequestTask();
rq.onResult(new Function<String, String>() {
@Override
public String apply(String s) {
matchTable(s);
return null;
}
});
rq.execute((TBAAddress + "event/" + matchKey + "/matches"), TBAHeader);
}
});
button.setText(matchKey);
tr.addView(button);
String name = j.getString("short_name");
// Sometimes, a short name is not present on TBA Events
if(name.isEmpty()){
name = j.getString("name");
}
addTableText(tr, name);
addTableText(tr, j.getString("event_type_string"));
// tr.addView(text);
Table.addView(tr);
toggle = !toggle;
}
}catch (JSONException j){
alert("Error", "Invalid JSON");
}
}
class matchComparator implements Comparator<JSONObject>
{
public int compare(JSONObject a, JSONObject b)
{
try {
return a.getInt("match_number") - b.getInt("match_number");
}catch (JSONException j){
return 0;
}
}
}
public void matchTable(String dataString){
try {
JSONArray data = new JSONArray(dataString);
Table.removeAllViews();
Table.setStretchAllColumns(true);
Table.bringToFront();
if(data.length() == 0){
TableRow tr = new TableRow(getContext());
addTableText(tr, "This event has no matches released yet...");
Table.addView(tr);
tr = new TableRow(getContext());
addTableText(tr, "Try manually adding practice matches.");
Table.addView(tr);
return;
}
TableRow tr = new TableRow(getContext());
addTableText(tr, "#");
addTableText(tr, "Red-1");
addTableText(tr, "Red-2");
addTableText(tr, "Red-3");
addTableText(tr, "Blue-1");
addTableText(tr, "Blue-2");
addTableText(tr, "Blue-3");
Table.addView(tr);
data = JSONUtil.sort(data, new Comparator(){
public int compare(Object a, Object b){
JSONObject ja = (JSONObject)a;
JSONObject jb = (JSONObject)b;
try {
return ja.getInt("match_number") - jb.getInt("match_number");
}catch (JSONException j){
return 0;
}
}
});
boolean toggle = false;
int matchCount = 1;
for(int a=0;a<data.length();a++){
final JSONObject match = data.getJSONObject(a);
if(!match.getString("comp_level").equals("qm")){
continue;
}
final JSONObject alliances = match.getJSONObject("alliances");
final JSONArray redAlliance = alliances.getJSONObject("red").getJSONArray("team_keys");
final JSONArray blueAlliance = alliances.getJSONObject("blue").getJSONArray("team_keys");
tr = new TableRow(getContext());
if (toggle) {
tr.setBackgroundColor(0x30000000);
}
addTableText(tr, String.valueOf(matchCount));
// addTableText(tr, match.getString("key"));
for(int b=0;b<6;b++){
TextView text = new TextView(getContext());
text.setTextSize(18);
text.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); // Text align center
tr.addView(text);
if(b < 3){
text.setText(redAlliance.getString(b).substring(3));
text.setBackgroundColor(0x50ff0000);
}else{
text.setText(blueAlliance.getString(b-3).substring(3));
text.setBackgroundColor(0x500000ff);
}
}
Table.addView(tr);
matchCount += 1;
toggle = !toggle;
}
}catch (JSONException j){
alert("Error", "Invalid JSON");
}
}
@Override
public void onDestroyView() {
File diff suppressed because one or more lines are too long
@@ -1,59 +0,0 @@
package com.astatin3.scoutingapp2025.ui.notifications;
import android.app.Activity;
import android.app.AlertDialog;
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 com.astatin3.scoutingapp2025.R;
import com.budiyev.android.codescanner.CodeScanner;
import com.budiyev.android.codescanner.CodeScannerView;
import com.budiyev.android.codescanner.DecodeCallback;
import com.budiyev.android.codescanner.ScanMode;
import com.google.zxing.DecodeHintType;
import com.google.zxing.EncodeHintType;
import com.google.zxing.Result;
import java.util.EnumMap;
import java.util.Hashtable;
import java.util.Map;
public class NotificationsFragment extends Fragment {
private CodeScanner mCodeScanner;
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();
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
final Activity activity = getActivity();
View root = inflater.inflate(R.layout.fragment_notifications, container, false);
IntentIntegrator integrator = IntentIntegrator.forSupportFragment(FragmentQRScan.this);
integrator.setOrientationLocked(false);
integrator.setPrompt("Scan QR code");
integrator.setBeepEnabled(false);
integrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE);
integrator.initiateScan();
return root;
}
}
@@ -0,0 +1,254 @@
package com.astatin3.scoutingapp2025.ui.transfer;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ScrollView;
import android.widget.TableRow;
import android.widget.TextView;
import androidx.annotation.NonNull;
import com.astatin3.scoutingapp2025.R;
import com.astatin3.scoutingapp2025.RequestTask;
import com.astatin3.scoutingapp2025.databinding.FragmentTbaBinding;
import com.astatin3.scoutingapp2025.databinding.FragmentTransferBinding;
import com.astatin3.scoutingapp2025.ui.JSONUtil;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Comparator;
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";
private android.widget.TableLayout Table;
public TBAView(Context context) {
super(context);
}
public TBAView(Context context, AttributeSet attributeSet){
super(context, attributeSet);
}
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 void start(FragmentTransferBinding binding, String yearStr) {
Table = binding.matchTable;
final RequestTask rq = new RequestTask();
rq.onResult(new Function<String, String>() {
@Override
public String apply(String s) {
eventTable(s);
return null;
}
});
rq.execute(TBAAddress + "events/"+yearStr, TBAHeader);
}
private void addTableText(TableRow tr, String textStr){
TextView text = new TextView(getContext());
text.setTextSize(18);
text.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); // Text align center
text.setText(textStr);
tr.addView(text);
}
public void eventTable(String dataString){
try {
JSONArray data = new JSONArray(dataString);
Table.setStretchAllColumns(true);
Table.bringToFront();
boolean toggle = false;
TableRow tr = new TableRow(getContext());
addTableText(tr, "Key");
addTableText(tr, "Title");
addTableText(tr, "Type");
Table.addView(tr);
for(int i=0;i<data.length();i++){
tr = new TableRow(getContext());
if (toggle) {
tr.setBackgroundColor(0x30000000);
}
JSONObject j = data.getJSONObject(i);
Button button = new Button(getContext());
String matchKey = j.getString("key");
button.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View v) {
final RequestTask rq = new RequestTask();
rq.onResult(new Function<String, String>() {
@Override
public String apply(String s) {
matchTable(s);
return null;
}
});
rq.execute((TBAAddress + "event/" + matchKey + "/matches"), TBAHeader);
}
});
button.setText(matchKey);
tr.addView(button);
String name = j.getString("short_name");
// Sometimes, a short name is not present on TBA Events
if(name.isEmpty()){
name = j.getString("name");
}
addTableText(tr, name);
addTableText(tr, j.getString("event_type_string"));
// tr.addView(text);
Table.addView(tr);
toggle = !toggle;
}
}catch (JSONException j){
alert("Error", "Invalid JSON");
}
}
class matchComparator implements Comparator<JSONObject>
{
public int compare(JSONObject a, JSONObject b)
{
try {
return a.getInt("match_number") - b.getInt("match_number");
}catch (JSONException j){
return 0;
}
}
}
public void matchTable(String dataString){
try {
JSONArray data = new JSONArray(dataString);
Table.removeAllViews();
Table.setStretchAllColumns(true);
Table.bringToFront();
if(data.length() == 0){
TableRow tr = new TableRow(getContext());
addTableText(tr, "This event has no matches released yet...");
Table.addView(tr);
tr = new TableRow(getContext());
addTableText(tr, "Try manually adding practice matches.");
Table.addView(tr);
return;
}
TableRow tr = new TableRow(getContext());
addTableText(tr, "#");
addTableText(tr, "Red-1");
addTableText(tr, "Red-2");
addTableText(tr, "Red-3");
addTableText(tr, "Blue-1");
addTableText(tr, "Blue-2");
addTableText(tr, "Blue-3");
Table.addView(tr);
data = JSONUtil.sort(data, new Comparator(){
public int compare(Object a, Object b){
JSONObject ja = (JSONObject)a;
JSONObject jb = (JSONObject)b;
try {
return ja.getInt("match_number") - jb.getInt("match_number");
}catch (JSONException j){
return 0;
}
}
});
boolean toggle = false;
int matchCount = 1;
for(int a=0;a<data.length();a++){
final JSONObject match = data.getJSONObject(a);
if(!match.getString("comp_level").equals("qm")){
continue;
}
final JSONObject alliances = match.getJSONObject("alliances");
final JSONArray redAlliance = alliances.getJSONObject("red").getJSONArray("team_keys");
final JSONArray blueAlliance = alliances.getJSONObject("blue").getJSONArray("team_keys");
tr = new TableRow(getContext());
if (toggle) {
tr.setBackgroundColor(0x30000000);
}
addTableText(tr, String.valueOf(matchCount));
// addTableText(tr, match.getString("key"));
for(int b=0;b<6;b++){
TextView text = new TextView(getContext());
text.setTextSize(18);
text.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); // Text align center
tr.addView(text);
if(b < 3){
text.setText(redAlliance.getString(b).substring(3));
text.setBackgroundColor(0x50ff0000);
}else{
text.setText(blueAlliance.getString(b-3).substring(3));
text.setBackgroundColor(0x500000ff);
}
}
Table.addView(tr);
matchCount += 1;
toggle = !toggle;
}
}catch (JSONException j){
alert("Error", "Invalid JSON");
}
}
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,188 @@
package com.astatin3.scoutingapp2025.ui.transfer;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.CountDownTimer;
import android.util.AttributeSet;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.astatin3.scoutingapp2025.R;
import com.astatin3.scoutingapp2025.databinding.FragmentTransferBinding;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.Map;
public class generatorView extends ConstraintLayout {
private FragmentTransferBinding binding;
private ImageView qrImage;
private SeekBar qrSpeedSlider;
private SeekBar qrSizeSlider;
private TextView qrIndexN;
private TextView qrIndexD;
private final int maxQrCount = 256; //The max number that can be stored in a byte
private final int maxQrSpeed = 20;
private final int minQrSpeed = 300 + maxQrSpeed - 1;
private int minQrSize = 0;
private final int maxQrSize = 600;
private int qrSize = 200;
private int curCodeIndex = 0;
private final int defaultQrDelay = 419;
private int qrDelay = 0;
private int qrIndex = 0;
private CountDownTimer timer;
private int qrCount = 0;
private ArrayList<Bitmap> qrBitmaps;
public generatorView(Context context) {
super(context);
}
public generatorView(Context context, AttributeSet attributeSet){
super(context, attributeSet);
}
public static Bitmap generateQrCode(String myCodeText) throws WriterException {
QRCodeWriter qrCodeWriter = new QRCodeWriter();
Map<EncodeHintType, Object> hints = new EnumMap<EncodeHintType, Object>(EncodeHintType.class);
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L); // H = 30% damage
// hints.put(EncodeHintType.QR_COMPACT, true);
hints.put(EncodeHintType.MARGIN, 0); /* default = 4 */
int size = 200;
BitMatrix bitMatrix = qrCodeWriter.encode(myCodeText, BarcodeFormat.QR_CODE, size, size, hints);
int width = bitMatrix.getWidth();
int height = bitMatrix.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] = bitMatrix.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;
}
public void start(FragmentTransferBinding binding, String data){
qrImage = binding.qrImage;
qrSpeedSlider = binding.qrSpeedSlider;
qrSizeSlider = binding.qrSizeSlider;
qrIndexN = binding.qrIndexN;
qrIndexD = binding.qrIndexD;
sendData(data);
}
private void sendData(String data){
minQrSize = Math.round(data.length()/maxQrCount);
qrSizeSlider.setMax(maxQrSize-minQrSize);
qrSpeedSlider.setMax((minQrSpeed-maxQrSpeed)*2);
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);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {}
});
qrSizeSlider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
qrSize = seekBar.getProgress() + minQrSpeed;
qrCount = (data.length()/qrSize)+1;
qrIndexD.setText(String.valueOf(qrCount));
sendData(data);
}
});
qrSpeedSlider.setProgress(defaultQrDelay+5);
qrBitmaps = new ArrayList<Bitmap>();
for(int i=0;i<=(data.length()/qrSize);i++){
final int start = i*qrSize;
int end = (i+1)*qrSize;
if(end > data.length()){
end = data.length()-1;
}
try {
qrBitmaps.add(generateQrCode(
data.substring(start, end)
));
}catch (WriterException e){
e.printStackTrace();
}
}
qrIndex = 0;
if(timer != null){
timer.cancel();
}
qrLoop();
}
private void updateQr(){
qrImage.setImageBitmap(qrBitmaps.get(qrIndex));
if(qrDelay > 0) {
this.qrIndex += 1;
if (this.qrIndex >= this.qrCount) {
this.qrIndex = 0;
}
}else{
this.qrIndex -= 1;
if (this.qrIndex < 0) {
this.qrIndex = this.qrCount-1;
}
}
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();
}
}.start();
}
}
@@ -0,0 +1,66 @@
package com.astatin3.scoutingapp2025.ui.transfer;
import android.app.ActionBar;
import android.content.Context;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.View;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.astatin3.scoutingapp2025.databinding.FragmentTransferBinding;
import com.astatin3.scoutingapp2025.qrPointsOverlayView;
import com.dlazaro66.qrcodereaderview.QRCodeReaderView;
public class scannerView extends ConstraintLayout {
private QRCodeReaderView qrCodeReaderView;
private qrPointsOverlayView pointsOverlayView;
private class codeReadListener implements QRCodeReaderView.OnQRCodeReadListener {
@Override
public void onQRCodeRead(String text, PointF[] points) {
pointsOverlayView.setPoints(points);
}
}
public scannerView(Context context) {
super(context);
}
public scannerView(Context context, AttributeSet attributeSet){
super(context, attributeSet);
}
public void start(FragmentTransferBinding binding){
qrCodeReaderView = new QRCodeReaderView(getContext());
this.addView(qrCodeReaderView);
ConstraintLayout.LayoutParams qrCodeReaderViewParams = (ConstraintLayout.LayoutParams) qrCodeReaderView.getLayoutParams();
qrCodeReaderViewParams.width = ActionBar.LayoutParams.MATCH_PARENT;
qrCodeReaderViewParams.height = ActionBar.LayoutParams.MATCH_PARENT;
qrCodeReaderView.setLayoutParams(qrCodeReaderViewParams);
pointsOverlayView = new qrPointsOverlayView(getContext());
pointsOverlayView.bringToFront();
this.addView(pointsOverlayView);
ConstraintLayout.LayoutParams pointsOverlayViewParams = (ConstraintLayout.LayoutParams) qrCodeReaderView.getLayoutParams();
pointsOverlayViewParams.width = ActionBar.LayoutParams.MATCH_PARENT;
pointsOverlayViewParams.height = ActionBar.LayoutParams.MATCH_PARENT;
pointsOverlayView.setLayoutParams(pointsOverlayViewParams);
qrCodeReaderView.startCamera();
// qrCodeReaderView = (QRCodeReaderView) binding.qrdecoderview;
qrCodeReaderView.setOnQRCodeReadListener(new codeReadListener());
// qrCodeReaderView.setQRDecodingEnabled(true);
qrCodeReaderView.setAutofocusInterval(2000L);
// qrCodeReaderView.setFrontCamera();
qrCodeReaderView.setBackCamera();
qrCodeReaderView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
qrCodeReaderView.forceAutoFocus();
}
});
}
}