diff --git a/README.md b/README.md index b3490a1..5ff50f2 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,36 @@ # ScoutingApp2025 Ridgebotics 2025 scouting app in Android -TODO: -- Add an "unselect" option to all of the scouting fields -- Make a word cloud for the compiled mode of notes input type -- Make pit and match data field builder UIs. I don't want to have to keep editing a variable -- Add "history" view type to the teams view menu. -- Add CSV exporting - +## TODO: +#### Scouting: - Make the "Compile" menu - The compile menu should be a shortcut to view all the team's stats from the upcoming match, from the teams view -- Make the file browser UI -- Fix the code scanning progress indicator -- Add more types of data fields. - -- Make server software to allow for easy sync over wifi - Make practice mode -- AI overview of scouting data for a team??? -- Bluetooth data sync +#### Data Analysis: +- Add CSV exporting - Statbotics intigration +- AI overview of scouting data for a team??? +#### Functionality: +- Improve the code scanning progress indicator. It has a rounding error, I think. +- Add more types of data fields. +- Make server software to allow for easy sync over wifi - Test the scouting app + +## 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 + + +## Done: +#### Scouting: +- Add an "unselect" option to all of the scouting fields +#### Data Analysis: +#### Functionality: +- Make the file browser UI +- Bluetooth data sync +- Formalize error messages & stacktraces \ No newline at end of file diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/MainActivity.java b/app/src/main/java/com/astatin3/scoutingapp2025/MainActivity.java index c45ef6f..d56cec2 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/MainActivity.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/MainActivity.java @@ -1,9 +1,11 @@ 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.utility.AlertManager; import com.astatin3.scoutingapp2025.utility.fileEditor; import com.google.android.material.bottomnavigation.BottomNavigationView; @@ -27,16 +29,6 @@ public class MainActivity extends AppCompatActivity { private BottomNavigationView navView; - private void alert(String title, String content) { - AlertDialog.Builder alert = new AlertDialog.Builder(this); - alert.setMessage(content); - alert.setTitle(title); - alert.setPositiveButton("OK", null); - alert.setCancelable(true); - alert.create().show(); - } - - @Override protected void onCreate(Bundle savedInstanceState) { @@ -50,6 +42,8 @@ public class MainActivity extends AppCompatActivity { fields.save(fields.pitsFieldsFilename, fields.default_pit_fields); } + AlertManager.init(this); + Objects.requireNonNull(getSupportActionBar()).hide(); super.onCreate(savedInstanceState); diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/SettingsVersionStack/settingsVersion.java b/app/src/main/java/com/astatin3/scoutingapp2025/SettingsVersionStack/settingsVersion.java index 7e79824..19c0bc3 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/SettingsVersionStack/settingsVersion.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/SettingsVersionStack/settingsVersion.java @@ -1,5 +1,6 @@ package com.astatin3.scoutingapp2025.SettingsVersionStack; +import com.astatin3.scoutingapp2025.utility.AlertManager; import com.astatin3.scoutingapp2025.utility.fileEditor; import java.nio.charset.StandardCharsets; @@ -54,7 +55,7 @@ public abstract class settingsVersion { } } }catch (Exception e){ - e.printStackTrace(); + AlertManager.error(e); } return null; diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/scoutingData/ScoutingDataWriter.java b/app/src/main/java/com/astatin3/scoutingapp2025/scoutingData/ScoutingDataWriter.java index 629c0e7..bd8d9f5 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/scoutingData/ScoutingDataWriter.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/scoutingData/ScoutingDataWriter.java @@ -6,6 +6,7 @@ import com.astatin3.scoutingapp2025.types.data.dataType; import com.astatin3.scoutingapp2025.types.data.stringType; import com.astatin3.scoutingapp2025.types.input.inputType; import com.astatin3.scoutingapp2025.types.data.intType; +import com.astatin3.scoutingapp2025.utility.AlertManager; import com.astatin3.scoutingapp2025.utility.fileEditor; import com.astatin3.scoutingapp2025.utility.BuiltByteParser; import com.astatin3.scoutingapp2025.utility.ByteBuilder; @@ -35,7 +36,7 @@ public class ScoutingDataWriter { fileEditor.writeFile(filename, bytes); return true; } catch (ByteBuilder.buildingException e) { - e.printStackTrace(); + AlertManager.error(e); return false; } } @@ -84,7 +85,7 @@ public class ScoutingDataWriter { return psda; } catch (BuiltByteParser.byteParsingExeption e){ - e.printStackTrace(); + AlertManager.error(e); return null; } } diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/scoutingData/fields.java b/app/src/main/java/com/astatin3/scoutingapp2025/scoutingData/fields.java index 53ffe98..e398324 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/scoutingData/fields.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/scoutingData/fields.java @@ -5,6 +5,7 @@ import com.astatin3.scoutingapp2025.types.input.inputType; import com.astatin3.scoutingapp2025.types.input.textType; import com.astatin3.scoutingapp2025.types.input.sliderType; import com.astatin3.scoutingapp2025.types.input.textType; +import com.astatin3.scoutingapp2025.utility.AlertManager; import com.astatin3.scoutingapp2025.utility.fileEditor; import com.astatin3.scoutingapp2025.utility.BuiltByteParser; import com.astatin3.scoutingapp2025.utility.ByteBuilder; @@ -54,7 +55,7 @@ public class fields { fileEditor.writeFile(filename, bb.build()); return true; }catch (ByteBuilder.buildingException e) { - e.printStackTrace(); + AlertManager.error(e); return false; // throw new RuntimeException(e); } @@ -86,7 +87,7 @@ public class fields { return values; // return true; } catch (Exception e) { - e.printStackTrace(); + AlertManager.error(e); return null; // return false; } diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/types/file.java b/app/src/main/java/com/astatin3/scoutingapp2025/types/file.java index 7c80363..c49269e 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/types/file.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/types/file.java @@ -1,5 +1,6 @@ package com.astatin3.scoutingapp2025.types; +import com.astatin3.scoutingapp2025.utility.AlertManager; import com.astatin3.scoutingapp2025.utility.BuiltByteParser; import com.astatin3.scoutingapp2025.utility.ByteBuilder; import com.astatin3.scoutingapp2025.utility.fileEditor; @@ -33,7 +34,7 @@ public class file { return bb.build(); } catch (ByteBuilder.buildingException e) { - e.printStackTrace(); + AlertManager.error(e); return null; } } @@ -50,7 +51,7 @@ public class file { return f; }catch (BuiltByteParser.byteParsingExeption e){ - e.printStackTrace(); + AlertManager.error(e); return null; } } diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/types/frcEvent.java b/app/src/main/java/com/astatin3/scoutingapp2025/types/frcEvent.java index 90e8ebc..8359572 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/types/frcEvent.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/types/frcEvent.java @@ -2,6 +2,7 @@ package com.astatin3.scoutingapp2025.types; import androidx.annotation.NonNull; +import com.astatin3.scoutingapp2025.utility.AlertManager; import com.astatin3.scoutingapp2025.utility.BuiltByteParser; import com.astatin3.scoutingapp2025.utility.ByteBuilder; @@ -31,7 +32,7 @@ public class frcEvent { return bb.build(); } catch (ByteBuilder.buildingException e) { - e.printStackTrace(); + AlertManager.error(e); return null; } } @@ -58,7 +59,7 @@ public class frcEvent { return frc; }catch (BuiltByteParser.byteParsingExeption e){ - e.printStackTrace(); + AlertManager.error(e); return null; } } diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/types/frcMatch.java b/app/src/main/java/com/astatin3/scoutingapp2025/types/frcMatch.java index e11bac1..eb351af 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/types/frcMatch.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/types/frcMatch.java @@ -2,6 +2,7 @@ package com.astatin3.scoutingapp2025.types; import androidx.annotation.NonNull; +import com.astatin3.scoutingapp2025.utility.AlertManager; import com.astatin3.scoutingapp2025.utility.BuiltByteParser; import com.astatin3.scoutingapp2025.utility.ByteBuilder; @@ -27,7 +28,7 @@ public class frcMatch { .addInt(redAlliance[2]) .build(); } catch (ByteBuilder.buildingException e) { - e.printStackTrace(); + AlertManager.error(e); return new byte[1]; } } @@ -49,7 +50,7 @@ public class frcMatch { return frc; } catch (BuiltByteParser.byteParsingExeption e) { - e.printStackTrace(); + AlertManager.error(e); return null; } } diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/types/frcTeam.java b/app/src/main/java/com/astatin3/scoutingapp2025/types/frcTeam.java index b12fe47..18a30df 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/types/frcTeam.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/types/frcTeam.java @@ -2,6 +2,7 @@ package com.astatin3.scoutingapp2025.types; import androidx.annotation.NonNull; +import com.astatin3.scoutingapp2025.utility.AlertManager; import com.astatin3.scoutingapp2025.utility.BuiltByteParser; import com.astatin3.scoutingapp2025.utility.ByteBuilder; @@ -33,7 +34,7 @@ public class frcTeam { .addInt(startingYear) .build(); } catch (ByteBuilder.buildingException e) { - e.printStackTrace(); + AlertManager.error(e); return null; } } @@ -54,7 +55,7 @@ public class frcTeam { return frc; } catch (BuiltByteParser.byteParsingExeption e) { - e.printStackTrace(); + AlertManager.error(e); return null; } } diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/ui/settings/settingsFragment.java b/app/src/main/java/com/astatin3/scoutingapp2025/ui/settings/settingsFragment.java index 426c035..23dff94 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/ui/settings/settingsFragment.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/ui/settings/settingsFragment.java @@ -36,15 +36,6 @@ public class settingsFragment extends Fragment { private android.widget.ScrollView ScrollArea; private android.widget.TableLayout Table; - 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 setDropdownItems(Spinner dropdown, String[] items){ ArrayAdapter adapter = new ArrayAdapter<>(requireActivity(), android.R.layout.simple_spinner_item, items); dropdown.setAdapter(adapter); 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 2aed0c7..cd0c298 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 @@ -12,6 +12,7 @@ import android.widget.TableLayout; import android.widget.TableRow; import android.widget.TextView; +import com.astatin3.scoutingapp2025.utility.AlertManager; import com.astatin3.scoutingapp2025.utility.RequestTask; import com.astatin3.scoutingapp2025.types.frcEvent; import com.astatin3.scoutingapp2025.types.frcMatch; @@ -46,16 +47,6 @@ public class TBAView extends ScrollView { 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; @@ -143,7 +134,7 @@ public class TBAView extends ScrollView { tr.setBackgroundColor(0x30FFFF00); } } catch (Exception e) { - e.printStackTrace(); + AlertManager.error(e); } @@ -179,7 +170,7 @@ public class TBAView extends ScrollView { toggle = !toggle; } }catch (JSONException j){ - alert("Error", "Invalid JSON"); + AlertManager.alert("Error", "Invalid JSON"); } } @@ -352,9 +343,9 @@ public class TBAView extends ScrollView { btn.setOnClickListener(v -> { if(saveData(matchesOBJ, teamData, eventData)){ - alert("Info", "Saved!"); + AlertManager.alert("Info", "Saved!"); }else{ - alert("Error", "Error saving files."); + AlertManager.alert("Error", "Error saving files."); } }); @@ -465,8 +456,8 @@ public class TBAView extends ScrollView { // }); }catch (JSONException j){ - j.printStackTrace(); - alert("Error", "Invalid JSON"); + AlertManager.error(j); + AlertManager.alert("Error", "Invalid JSON"); } } @@ -505,8 +496,8 @@ public class TBAView extends ScrollView { return fileEditor.setEvent(event); }catch (JSONException j){ - j.printStackTrace(); - alert("Error", "Invalid JSON"); + AlertManager.error(j); + AlertManager.alert("Error", "Invalid JSON"); return false; } } 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 24845f3..6881d6d 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 @@ -99,8 +99,8 @@ public class TransferFragment extends Fragment { public void show_ui(){ binding.mainSelectLayout.setVisibility(View.VISIBLE); - binding.noEventError.setVisibility(View.GONE); + binding.loadSelectLayout.setVisibility(View.GONE); binding.bluetoothSenderView.setVisibility(View.GONE); binding.bluetoothReceiverView.setVisibility(View.GONE); @@ -313,6 +313,7 @@ public class TransferFragment extends Fragment { private void start_upload_bluetooth(byte[] data){ binding.loadSelectLayout.setVisibility(View.GONE); binding.bluetoothSenderView.setVisibility(View.VISIBLE); + binding.bluetoothSenderView.init(); binding.bluetoothSenderView.set_data(data); @@ -321,6 +322,7 @@ public class TransferFragment extends Fragment { private void start_download_bluetooth(){ binding.loadSelectLayout.setVisibility(View.GONE); binding.bluetoothReceiverView.setVisibility(View.VISIBLE); + binding.bluetoothReceiverView.init(); } diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/bluetooth/BluetoothReceiver.java b/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/bluetooth/BluetoothReceiver.java index 17e6b0d..0f1ba69 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/bluetooth/BluetoothReceiver.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/bluetooth/BluetoothReceiver.java @@ -11,6 +11,8 @@ import android.os.Build; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; +import com.astatin3.scoutingapp2025.utility.AlertManager; + import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; @@ -48,7 +50,8 @@ public class BluetoothReceiver { private static final int REQUEST_BLUETOOTH_PERMISSIONS = 1; - public static void requestBluetoothPermissions(Activity activity) { + public static void + requestBluetoothPermissions(Activity activity) { List permissionsNeeded = new ArrayList<>(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { @@ -116,28 +119,36 @@ public class BluetoothReceiver { // Handle incoming data here handleIncomingData(); } catch (IOException e) { - e.printStackTrace(); + AlertManager.error(e); } } } }).start(); } - private void handleIncomingData() throws IOException { + private void handleIncomingData() throws IOException { byte[] buffer = new byte[1024]; int bytes; - while (true) { - bytes = inputStream.read(buffer); - if (bytes != -1) { - // Process received data here -// receiveddata.processReceivedData(buffer, bytes); - System.out.println(Arrays.toString(buffer)); + try { + while (true) { + bytes = inputStream.read(buffer); + if (bytes != -1) { + receiveddata.processReceivedData(buffer, bytes); + } } + } catch (IOException e) { + if (e.getMessage() != null && e.getMessage().contains("bt socket closed, read return: -1")) { + receiveddata.onConnectionStop(); + System.out.println("Bluetooth socket closed, treating as end of stream"); + } else { + throw e; } } + } public interface receivedData { public void processReceivedData(byte[] data, int bytes); + public void onConnectionStop(); } diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/bluetooth/BluetoothReceiverView.java b/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/bluetooth/BluetoothReceiverView.java index bbb3555..9a87a56 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/bluetooth/BluetoothReceiverView.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/bluetooth/BluetoothReceiverView.java @@ -1,20 +1,28 @@ package com.astatin3.scoutingapp2025.ui.transfer.bluetooth; +import android.app.AlertDialog; import android.content.Context; +import android.os.Looper; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; -import android.widget.Toast; +import com.astatin3.scoutingapp2025.MainActivity; import com.astatin3.scoutingapp2025.R; +import com.astatin3.scoutingapp2025.types.file; +import com.astatin3.scoutingapp2025.utility.AlertManager; +import com.astatin3.scoutingapp2025.utility.BuiltByteParser; +import com.astatin3.scoutingapp2025.utility.fileEditor; -import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; +import java.util.zip.DataFormatException; public class BluetoothReceiverView extends LinearLayout { private BluetoothReceiver bluetoothReceiver; @@ -24,23 +32,39 @@ public class BluetoothReceiverView extends LinearLayout { public BluetoothReceiverView(Context context) { super(context); - init(context); +// init(context); } public BluetoothReceiverView(Context context, AttributeSet attrs) { super(context, attrs); - init(context); +// init(context); } - private void init(Context context) { - LayoutInflater.from(context).inflate(R.layout.view_bluetooth_receiver, this, true); +// private void alert(String title, String content) { +// AlertDialog.Builder dialog = new AlertDialog.Builder(getContext()); +// dialog.setCancelable(true); +// dialog.setTitle(title); +// dialog.setMessage(content); +// +// final AlertDialog alert = dialog.create(); +// alert.show(); +// +// } + + public void init() { + LayoutInflater.from(getContext()).inflate(R.layout.view_bluetooth_receiver, this, true); // bluetoothReceiver = new BluetoothReceiver(context); - bluetoothReceiver = new BluetoothReceiver(context, new BluetoothReceiver.receivedData() { + bluetoothReceiver = new BluetoothReceiver(getContext(), new BluetoothReceiver.receivedData() { @Override public void processReceivedData(byte[] data, int bytes) { - receiveFile(data,bytes); + receiveData(data, bytes); + } + + @Override + public void onConnectionStop() { + finished_recieve(); } }); @@ -49,12 +73,12 @@ public class BluetoothReceiverView extends LinearLayout { statusTextView = findViewById(R.id.statusTextView); if (!bluetoothReceiver.isBluetoothSupported()) { - Toast.makeText(context, "Bluetooth is not supported on this device", Toast.LENGTH_SHORT).show(); + AlertManager.error("Bluetooth is not supported on this device"); return; } if (!bluetoothReceiver.isBluetoothEnabled()) { - Toast.makeText(context, "Please enable Bluetooth", Toast.LENGTH_SHORT).show(); + AlertManager.error("Please enable Bluetooth"); } startListeningButton.setOnClickListener(new OnClickListener() { @@ -78,8 +102,11 @@ public class BluetoothReceiverView extends LinearLayout { statusTextView.setText("Listening for incoming connections..."); startListeningButton.setEnabled(false); stopListeningButton.setEnabled(true); + + recievedBytes = new ArrayList<>(); + } catch (IOException e) { - Toast.makeText(getContext(), "Failed to start listening: " + e.getMessage(), Toast.LENGTH_SHORT).show(); + AlertManager.error("Failed to start listening: " + e.getMessage()); } } @@ -90,19 +117,57 @@ public class BluetoothReceiverView extends LinearLayout { startListeningButton.setEnabled(true); stopListeningButton.setEnabled(false); } catch (IOException e) { - Toast.makeText(getContext(), "Failed to stop listening: " + e.getMessage(), Toast.LENGTH_SHORT).show(); + AlertManager.error("Failed to stop listening: " + e.getMessage()); } } - private void receiveFile(byte[] data, int bytes) { - System.out.println(Arrays.toString(data) + ", " + bytes); + private List recievedBytes; + + private void receiveData(byte[] data, int bytes) { + byte[] newBytes = fileEditor.getByteBlock(data, 0, bytes); + System.out.println("Recieved " + bytes + " Bytes over bluetooth!"); + recievedBytes.add(newBytes); } + + private void finished_recieve() { + String result_filenames = ""; + try { + + byte[] resultBytes = fileEditor.combineByteArrays(recievedBytes); + resultBytes = fileEditor.blockUncompress(resultBytes); + + + BuiltByteParser bbp = new BuiltByteParser(resultBytes); + ArrayList result = bbp.parse(); + + for (int i = 0; i < result.size(); i++) { + if (result.get(i).getType() != file.typecode) continue; + file f = file.decode((byte[]) result.get(i).get()); + + if (f != null) { + System.out.println(f.filename); + if (f.write()) + result_filenames += f.filename + "\n"; + } + } + + } catch (DataFormatException e) { + AlertManager.error(e); + } catch (BuiltByteParser.byteParsingExeption e) { + AlertManager.error(e); + } + + AlertManager.alert("Completed!", result_filenames); + } + + public void onDestroy() { - try { - bluetoothReceiver.stopListening(); - } catch (IOException e) { - e.printStackTrace(); - } + if (bluetoothReceiver != null) + try { + bluetoothReceiver.stopListening(); + } catch (IOException e) { + AlertManager.error(e); + } } } \ No newline at end of file diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/bluetooth/BluetoothSender.java b/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/bluetooth/BluetoothSender.java index e1eab04..6f016b0 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/bluetooth/BluetoothSender.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/bluetooth/BluetoothSender.java @@ -11,6 +11,8 @@ import android.os.Build; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; +import com.astatin3.scoutingapp2025.utility.AlertManager; + import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; @@ -26,7 +28,7 @@ public class BluetoothSender { private Context context; private BluetoothAdapter bluetoothAdapter; private BluetoothSocket socket; - private OutputStream outputStream; + public OutputStream outputStream; public BluetoothSender(Context context) { this.context = context; @@ -120,8 +122,14 @@ public class BluetoothSender { public void close() throws IOException { if (outputStream != null) { + outputStream.flush(); outputStream.close(); } + try { + Thread.sleep(500); + } catch (InterruptedException e) { + AlertManager.error(e); + } if (socket != null) { socket.close(); } diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/bluetooth/BluetoothSenderView.java b/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/bluetooth/BluetoothSenderView.java index 6238612..f272fee 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/bluetooth/BluetoothSenderView.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/bluetooth/BluetoothSenderView.java @@ -10,16 +10,13 @@ import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.LinearLayout; import android.widget.ListView; -import android.widget.Toast; import com.astatin3.scoutingapp2025.R; +import com.astatin3.scoutingapp2025.utility.AlertManager; import com.astatin3.scoutingapp2025.utility.fileEditor; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Set; public class BluetoothSenderView extends LinearLayout { @@ -32,36 +29,34 @@ public class BluetoothSenderView extends LinearLayout { public BluetoothSenderView(Context context) { super(context); - init(context); } public BluetoothSenderView(Context context, AttributeSet attrs) { super(context, attrs); - init(context); } public void set_data(byte[] data){ data_to_send = data; } - private void init(Context context) { - LayoutInflater.from(context).inflate(R.layout.view_bluetooth_sender, this, true); + public void init() { + LayoutInflater.from(getContext()).inflate(R.layout.view_bluetooth_sender, this, true); - bluetoothSender = new BluetoothSender(context); + bluetoothSender = new BluetoothSender(getContext()); deviceListView = findViewById(R.id.deviceListView); sendFileButton = findViewById(R.id.sendFileButton); deviceList = new ArrayList<>(); - deviceArrayAdapter = new ArrayAdapter<>(context, android.R.layout.simple_list_item_1); + deviceArrayAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_list_item_1); deviceListView.setAdapter(deviceArrayAdapter); if (!bluetoothSender.isBluetoothSupported()) { - Toast.makeText(context, "Bluetooth is not supported on this device", Toast.LENGTH_SHORT).show(); + AlertManager.toast("Bluetooth is not supported on this device"); return; } if (!bluetoothSender.isBluetoothEnabled()) { - Toast.makeText(context, "Please enable Bluetooth", Toast.LENGTH_SHORT).show(); + AlertManager.toast("Please enable Bluetooth"); } else { listPairedDevices(); } @@ -72,10 +67,10 @@ public class BluetoothSenderView extends LinearLayout { BluetoothDevice selectedDevice = deviceList.get(position); try { bluetoothSender.connectToDevice(selectedDevice); - Toast.makeText(context, "Connected to " + selectedDevice.getName(), Toast.LENGTH_SHORT).show(); + AlertManager.toast("Connected to " + selectedDevice.getName()); sendFileButton.setEnabled(true); } catch (IOException e) { - Toast.makeText(context, "Failed to connect: " + e.getMessage(), Toast.LENGTH_SHORT).show(); + AlertManager.toast("Failed to connect: " + e.getMessage()); } } }); @@ -83,9 +78,7 @@ public class BluetoothSenderView extends LinearLayout { sendFileButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - // You would typically launch a file picker here - // For this example, we'll just send a dummy file - sendDummyFile(); + sendData(); } }); } @@ -98,29 +91,35 @@ public class BluetoothSenderView extends LinearLayout { deviceArrayAdapter.add(device.getName() + "\n" + device.getAddress()); } } else { - Toast.makeText(getContext(), "No paired devices found", Toast.LENGTH_SHORT).show(); + AlertManager.toast("No paired devices found"); } } - private void sendDummyFile() { + private void sendData() { try { - for(int i = 0; i < Math.floor((double) data_to_send.length /1024); i++){ - bluetoothSender.sendData(fileEditor.getByteBlock(data_to_send, (i*1024), (i+1)*1024)); -// System.out.println(Arrays.toString(buffer)); + byte[] compressed = fileEditor.blockCompress(data_to_send); + + for(int i = 0; i < Math.ceil((double) compressed.length/1024); i++){ + bluetoothSender.sendData(fileEditor.getByteBlock(compressed, i*1024, (i+1)*1024)); } + bluetoothSender.close(); + sendFileButton.setEnabled(false); - Toast.makeText(getContext(), "File sent successfully", Toast.LENGTH_SHORT).show(); + + + AlertManager.toast("File sent successfully"); } catch (IOException e) { - Toast.makeText(getContext(), "Failed to send file: " + e.getMessage(), Toast.LENGTH_SHORT).show(); + AlertManager.toast("Failed to send file: " + e.getMessage()); } } public void onDestroy() { - try { - bluetoothSender.close(); - } catch (IOException e) { - e.printStackTrace(); - } + if(bluetoothSender != null) + try { + bluetoothSender.close(); + } catch (IOException e) { + AlertManager.error(e); + } } } \ No newline at end of file diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/codes/codeScanTask.java b/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/codes/codeScanTask.java index ec78c58..e9c1815 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/codes/codeScanTask.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/codes/codeScanTask.java @@ -3,6 +3,7 @@ package com.astatin3.scoutingapp2025.ui.transfer.codes; import android.graphics.Bitmap; import android.os.AsyncTask; +import com.astatin3.scoutingapp2025.utility.AlertManager; import com.google.zxing.BarcodeFormat; import com.google.zxing.BinaryBitmap; import com.google.zxing.ChecksumException; @@ -46,7 +47,7 @@ public class codeScanTask extends AsyncTask{ Result result = reader.decode(binaryBitmap, hints); return result.getText(); } catch (NotFoundException | ChecksumException | FormatException e) { - e.printStackTrace(); +// AlertManager.error(e); } return null; diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/codes/generatorView.java b/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/codes/generatorView.java index cebd7f2..b44dc30 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/codes/generatorView.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/codes/generatorView.java @@ -13,6 +13,7 @@ import android.widget.TextView; import androidx.constraintlayout.widget.ConstraintLayout; import com.astatin3.scoutingapp2025.databinding.FragmentTransferBinding; +import com.astatin3.scoutingapp2025.utility.AlertManager; import com.astatin3.scoutingapp2025.utility.fileEditor; import com.google.zxing.BarcodeFormat; import com.google.zxing.EncodeHintType; @@ -60,16 +61,6 @@ public class generatorView extends ConstraintLayout { 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(); - } - - private Bitmap generateQrCode(String contents) throws WriterException { final int size = 512; @@ -91,7 +82,7 @@ public class generatorView extends ConstraintLayout { result = writer.encode(contents, BarcodeFormat.DATA_MATRIX, size, size, hints); } catch (IllegalArgumentException e) { // Unsupported format - e.printStackTrace(); + AlertManager.error(e); return null; } @@ -122,39 +113,14 @@ public class generatorView extends ConstraintLayout { qrIndexN = binding.qrIndexN; qrIndexD = binding.qrIndexD; + String compressed = new String(fileEditor.blockCompress(inputData), StandardCharsets.ISO_8859_1); - String compiledData = ""; - - for(int i=0;i inputData.length) { - end = inputData.length; - } - - byte[] dataBlock = fileEditor.getByteBlock(inputData, start, end); - - final String compressedBlock = - new String( - fileEditor.compress(dataBlock), - StandardCharsets.ISO_8859_1); - - compiledData += - new String( - fileEditor.toBytes(compressedBlock.length(), 2), - StandardCharsets.ISO_8859_1) + - - compressedBlock; - - - } - - if(compiledData.isEmpty()){ - alert("Error!", "Empty data!"); + if(compressed.isEmpty()){ + AlertManager.alert("Error!", "Empty data!"); return; } - minQrSize = Math.round(compiledData.length()/maxQrCount)+1; + minQrSize = Math.round((float)compressed.length() / maxQrCount)+1; qrSizeSlider.setMax(maxQrSize-minQrSize); qrSpeedSlider.setMax((minQrSpeed-maxQrSpeed)*2); @@ -162,7 +128,7 @@ public class generatorView extends ConstraintLayout { qrSizeSlider.setProgress(minQrSize+qrSize); qrSpeedSlider.setProgress(defaultQrDelay+5); - sendData(compiledData); + sendData(compressed); } private void sendData(String data){ @@ -223,7 +189,7 @@ public class generatorView extends ConstraintLayout { )); // alert("title", ""+(qrCount-1)); }catch (WriterException e){ - e.printStackTrace(); + AlertManager.error(e); } } qrIndex = 0; diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/codes/scannerView.java b/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/codes/scannerView.java index fd8e46d..e5b5a58 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/codes/scannerView.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/ui/transfer/codes/scannerView.java @@ -27,6 +27,7 @@ import androidx.lifecycle.LifecycleOwner; import com.astatin3.scoutingapp2025.databinding.FragmentTransferBinding; import com.astatin3.scoutingapp2025.types.file; +import com.astatin3.scoutingapp2025.utility.AlertManager; import com.astatin3.scoutingapp2025.utility.BuiltByteParser; import com.astatin3.scoutingapp2025.utility.fileEditor; import com.google.common.util.concurrent.ListenableFuture; @@ -305,7 +306,8 @@ public class scannerView extends ConstraintLayout { try { byte[] compiledBytes = compiledString.getBytes(StandardCharsets.ISO_8859_1); - byte[] resultBytes = blockUncompress(compiledBytes); + byte[] resultBytes = fileEditor.blockUncompress(compiledBytes); + String result_filenames = ""; @@ -321,30 +323,14 @@ public class scannerView extends ConstraintLayout { result_filenames += f.filename + "\n"; } - alert("Completed!", result_filenames); + AlertManager.alert("Completed!", result_filenames); }catch (Exception e){ - e.printStackTrace(); + AlertManager.error(e); } } prevQrIndex = qrIndex; } - private static byte[] blockUncompress(byte[] data) throws DataFormatException { - List uncompressedData = new ArrayList<>(); - int curIndex = 0; - while(curIndex < data.length){ - final int blockLength = fileEditor.fromBytes(fileEditor.getByteBlock(data, curIndex, curIndex+2), 2); - - uncompressedData.add( - fileEditor.decompress( - fileEditor.getByteBlock(data, curIndex+2, curIndex+blockLength+2) - ) - ); - - curIndex += blockLength+2; - } - return fileEditor.combineByteArrays(uncompressedData); - } } diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/utility/AlertManager.java b/app/src/main/java/com/astatin3/scoutingapp2025/utility/AlertManager.java new file mode 100644 index 0000000..4b77611 --- /dev/null +++ b/app/src/main/java/com/astatin3/scoutingapp2025/utility/AlertManager.java @@ -0,0 +1,72 @@ +package com.astatin3.scoutingapp2025.utility; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.Context; +import android.os.Looper; +import android.widget.Toast; + +import java.io.PrintWriter; +import java.io.StringWriter; + +public class AlertManager { + private static Context context; + + public static void init(Context c){ + context = c; + } + + public static void alert(String title, String content) { + ((Activity) context).runOnUiThread(new Runnable() { + public void run() { + AlertDialog.Builder alert = new AlertDialog.Builder(context); + alert.setMessage(content); + alert.setTitle(title); + alert.setPositiveButton("OK", null); + alert.setCancelable(true); + + alert.create().show(); + } + }); + } + + public static void toast(String content) { + ((Activity) context).runOnUiThread(new Runnable() { + public void run() { + Toast.makeText(context, content, Toast.LENGTH_LONG).show(); + } + }); + } + + public static void error(String content) { + ((Activity) context).runOnUiThread(new Runnable() { + public void run() { + AlertDialog.Builder alert = new AlertDialog.Builder(context); + alert.setMessage(content); + alert.setTitle("Error!"); + alert.setPositiveButton("OK", null); + alert.setCancelable(true); + + alert.create().show(); + } + }); + } + + public static void error(Exception e) { + ((Activity) context).runOnUiThread(new Runnable() { + public void run() { + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + + AlertDialog.Builder alert = new AlertDialog.Builder(context); + alert.setMessage(sw.toString()); + alert.setTitle(e.getMessage()); + alert.setPositiveButton("OK", null); + alert.setCancelable(true); + + alert.create().show(); + } + }); + } + +} diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/utility/RequestTask.java b/app/src/main/java/com/astatin3/scoutingapp2025/utility/RequestTask.java index dac06db..63207ff 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/utility/RequestTask.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/utility/RequestTask.java @@ -37,7 +37,7 @@ public class RequestTask extends AsyncTask { return null; // See documentation for more info on response handling } } catch (IOException e) { - e.printStackTrace(); + AlertManager.error(e); } return null; } @@ -51,13 +51,13 @@ public class RequestTask extends AsyncTask { response.append(line); } } catch (IOException e) { - e.printStackTrace(); + AlertManager.error(e); } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { - e.printStackTrace(); + AlertManager.error(e); } } } diff --git a/app/src/main/java/com/astatin3/scoutingapp2025/utility/fileEditor.java b/app/src/main/java/com/astatin3/scoutingapp2025/utility/fileEditor.java index 6cae2d0..7e45b27 100644 --- a/app/src/main/java/com/astatin3/scoutingapp2025/utility/fileEditor.java +++ b/app/src/main/java/com/astatin3/scoutingapp2025/utility/fileEditor.java @@ -82,6 +82,8 @@ public final class fileEditor { public static byte[] getByteBlock(byte[] bytes, int start, int end){ + end = Math.min(end, bytes.length); + byte[] dataBlock = new byte[end-start]; for(int a=start;a compiledData = new ArrayList<>(); + + for(int i=0;i inputData.length) { + end = inputData.length; + } + + byte[] dataBlock = fileEditor.getByteBlock(inputData, start, end); + + final byte[] compressedBlock = fileEditor.compress(dataBlock); + + compiledData.add(fileEditor.toBytes(compressedBlock.length, 2)); + compiledData.add(compressedBlock); + } + return combineByteArrays(compiledData); + } + + public static byte[] blockUncompress(byte[] data) throws DataFormatException { + List uncompressedData = new ArrayList<>(); + int curIndex = 0; + while (curIndex < data.length) { + + final int blockLength = fileEditor.fromBytes(fileEditor.getByteBlock(data, curIndex, curIndex + 2), 2); + + uncompressedData.add( + decompress( + fileEditor.getByteBlock(data, curIndex + 2, curIndex + blockLength + 2) + ) + ); + + curIndex += blockLength + 2; + } + return combineByteArrays(uncompressedData); + } + public static byte[] decompress(byte[] input) throws DataFormatException { Inflater inflater = new Inflater(); @@ -163,7 +203,7 @@ public final class fileEditor { return true; } catch (IOException e) { - e.printStackTrace(); + AlertManager.error(e); return false; } } @@ -177,7 +217,7 @@ public final class fileEditor { return file.createNewFile(); } catch (IOException e) { - e.printStackTrace(); + AlertManager.error(e); return false; } } @@ -197,10 +237,10 @@ public final class fileEditor { buf.close(); return bytes; } catch (FileNotFoundException e) { - e.printStackTrace(); + AlertManager.error(e); return null; } catch (IOException e) { - e.printStackTrace(); + AlertManager.error(e); return null; } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ab8f148..8aa1fd6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -agp = "8.3.1" +agp = "8.5.0" junit = "4.13.2" junitVersion = "1.1.5" espressoCore = "3.5.1" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7e84399..3addd18 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Sun Mar 24 10:48:55 MDT 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists