mirror of
https://github.com/Team4388/RidgeScout.git
synced 2026-06-09 00:37:59 -06:00
Add file bundles.
Start work on ftp support.
This commit is contained in:
@@ -6,27 +6,25 @@ Ridgebotics 2025 scouting app in Android
|
||||
- Make the "Report" menu, A tool that lets users select data to display from the the teams and compare menus.
|
||||
- Make practice mode
|
||||
#### Data Analysis:
|
||||
- Statbotics intigration
|
||||
- Statbotics intigration???
|
||||
- AI overview of scouting data for a team???
|
||||
- Make the "Compare" menu, cross comparing team's stats.
|
||||
#### Functionality:
|
||||
- Make the system for blank and unselected fields better.
|
||||
- Add more types of data fields.
|
||||
- Make server software to allow for easy sync over wifi
|
||||
- Test the scouting app
|
||||
- Deploy to F-Droid
|
||||
|
||||
## In Progress:
|
||||
#### Scouting:
|
||||
- When a field is created, make updated scouting data return null values, not the default value
|
||||
- Fix scouting offset bug
|
||||
#### Data Analysis:
|
||||
#### Functionality:
|
||||
|
||||
- Make server software to allow for easy sync over wifi - FTP
|
||||
|
||||
## Done:
|
||||
#### Scouting:
|
||||
- Add an "unselect" option to all of the scouting fields
|
||||
- When a field is created, make updated scouting data return null values, not the default value
|
||||
- Fix scouting offset bug
|
||||
#### Data Analysis:
|
||||
- Add "history" view type to the teams view menu.
|
||||
- Sentiment analysis of text input type
|
||||
@@ -39,3 +37,4 @@ Ridgebotics 2025 scouting app in Android
|
||||
- Bluetooth data sync
|
||||
- Formalize error messages & stacktraces
|
||||
- Make pit and match data field builder UIs. I don't want to have to keep editing a variable
|
||||
- Make the system for blank and unselected fields better.
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
android:name="android.hardware.camera"
|
||||
android:required="true" />
|
||||
|
||||
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
||||
@@ -36,10 +39,14 @@
|
||||
android:label="@string/app_name">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
<action android:name="android.intent.action.GET_CONTENT" />
|
||||
<category android:name="android.intent.category.OPEN_FILE" />
|
||||
<data android:mimeType="*/*" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.transfer.FileBundle$FileDialogActivity" />
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.astatin3.scoutingapp2025;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
|
||||
@@ -136,4 +137,26 @@ public class MainActivity extends AppCompatActivity {
|
||||
private void clearBackStack() {
|
||||
navController.popBackStack(navController.getGraph().getStartDestinationId(), false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public interface activityResultRelay {
|
||||
void onActivityResult(int requestCode, int resultCode, Intent data);
|
||||
}
|
||||
|
||||
public static activityResultRelay resultRelay = null;
|
||||
public static void setResultRelay(activityResultRelay tmpresultRelay){
|
||||
resultRelay = tmpresultRelay;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
// AlertManager.error(String.valueOf(requestCode));
|
||||
if (resultRelay != null) {
|
||||
resultRelay.onActivityResult(resultCode, requestCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -229,7 +229,7 @@ public class dropdownType extends inputType {
|
||||
for(int i = 0; i < text_options.length; i++){
|
||||
List<Entry> entries = new ArrayList<>();
|
||||
for (int a = 0; a < data.length; a++) {
|
||||
if(data[a] == null) continue;
|
||||
if(data[a].isNull()) continue;
|
||||
|
||||
entries.add(
|
||||
new Entry(a,
|
||||
|
||||
@@ -186,7 +186,7 @@ public class sliderType extends inputType {
|
||||
int[] values = new int[max-min+1];
|
||||
|
||||
for (int i = 0; i < data.length; i++)
|
||||
if(intType.isNull((int) data[i].get()))
|
||||
if(!data[i].isNull())
|
||||
values[(int) data[i].get()-min]++;
|
||||
|
||||
|
||||
@@ -262,6 +262,7 @@ public class sliderType extends inputType {
|
||||
List<Entry> entries = new ArrayList<>();
|
||||
for (int i = 0; i < data.length; i++){
|
||||
if(data[i] == null) continue;
|
||||
if(data[i].isNull()) continue;
|
||||
|
||||
entries.add(new Entry(i, (float)(int) data[i].get()));
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ public class tallyType extends inputType {
|
||||
|
||||
|
||||
public void add_individual_view(LinearLayout parent, dataType data){
|
||||
if((int)data.get() == 0) return;
|
||||
if(data.isNull()) return;
|
||||
|
||||
TextView tv = new TextView(parent.getContext());
|
||||
tv.setLayoutParams(new FrameLayout.LayoutParams(
|
||||
@@ -176,7 +176,7 @@ public class tallyType extends inputType {
|
||||
int[] values = new int[max-min+1];
|
||||
|
||||
for (int i = 0; i < data.length; i++)
|
||||
if(intType.isNull((int) data[i].get()))
|
||||
if(data[i] != null && data[i].isNull())
|
||||
values[(int) data[i].get()-min]++;
|
||||
|
||||
|
||||
@@ -255,6 +255,7 @@ public class tallyType extends inputType {
|
||||
List<Entry> entries = new ArrayList<>();
|
||||
for (int i = 0; i < data.length; i++){
|
||||
if(data[i] == null) continue;
|
||||
if(data[i].isNull()) continue;
|
||||
|
||||
entries.add(new Entry(i, (float)(int) data[i].get()));
|
||||
}
|
||||
|
||||
@@ -183,6 +183,7 @@ public class textType extends inputType {
|
||||
List<Entry> entries = new ArrayList<>();
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
if(data[i] == null) continue;
|
||||
if(data[i].isNull()) continue;
|
||||
|
||||
entries.add(
|
||||
new Entry(i,
|
||||
|
||||
@@ -4,6 +4,7 @@ import static com.astatin3.scoutingapp2025.utility.DataManager.evcode;
|
||||
import static com.astatin3.scoutingapp2025.utility.DataManager.event;
|
||||
import static com.astatin3.scoutingapp2025.utility.DataManager.match_latest_values;
|
||||
import static com.astatin3.scoutingapp2025.utility.DataManager.pit_latest_values;
|
||||
import static com.astatin3.scoutingapp2025.utility.SharePrompt.shareContent;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -122,29 +123,4 @@ public class CSVExport {
|
||||
|
||||
shareContent(c, evcode+"-pits.csv", data, "text/plain");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static void shareContent(Context context, String fileName, String content, String mimeType) {
|
||||
try {
|
||||
File file = new File(context.getCacheDir(), fileName);
|
||||
FileOutputStream fos = new FileOutputStream(file);
|
||||
fos.write(content.getBytes());
|
||||
fos.close();
|
||||
|
||||
Uri fileUri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", file);
|
||||
|
||||
Intent shareIntent = new Intent(Intent.ACTION_SEND);
|
||||
shareIntent.setType(mimeType);
|
||||
shareIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
|
||||
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
|
||||
context.startActivity(Intent.createChooser(shareIntent, "Share using"));
|
||||
} catch (IOException e) {
|
||||
AlertManager.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
package com.astatin3.scoutingapp2025.ui.transfer;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.astatin3.scoutingapp2025.MainActivity;
|
||||
import com.astatin3.scoutingapp2025.types.file;
|
||||
import com.astatin3.scoutingapp2025.utility.AlertManager;
|
||||
import com.astatin3.scoutingapp2025.utility.BuiltByteParser;
|
||||
import com.astatin3.scoutingapp2025.utility.ByteBuilder;
|
||||
import com.astatin3.scoutingapp2025.utility.DataManager;
|
||||
import com.astatin3.scoutingapp2025.utility.SharePrompt;
|
||||
import com.astatin3.scoutingapp2025.utility.fileEditor;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class FileBundle {
|
||||
private static final Intent FILE_SELECT_CODE = new Intent();
|
||||
|
||||
public static void send(String[] files, Context c){
|
||||
try {
|
||||
ByteBuilder b = new ByteBuilder();
|
||||
|
||||
for(int i = 0; i < files.length; i++){
|
||||
if(!fileEditor.fileExist(files[i])) continue;
|
||||
// byte[] data = fileEditor.readFile(files[i]);
|
||||
file f = new file(files[i]);
|
||||
b.addRaw(file.typecode, f.encode());
|
||||
}
|
||||
|
||||
byte[] data = b.build();
|
||||
send(data, c);
|
||||
|
||||
} catch (ByteBuilder.buildingException e) {
|
||||
AlertManager.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void send(byte[] data, Context c){
|
||||
String filename = DataManager.getevcode() + "-" + System.currentTimeMillis() + ".scoutbundle";
|
||||
SharePrompt.shareContent(c, filename, data, "application/ridgescout");
|
||||
}
|
||||
|
||||
|
||||
public static void receive(Activity b){
|
||||
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
intent.setType("*/*");
|
||||
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
|
||||
|
||||
MainActivity.setResultRelay(new MainActivity.activityResultRelay() {
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
Uri uri = data.getData();
|
||||
if(uri == null) return;
|
||||
|
||||
try (InputStream is = b.getContentResolver().openInputStream(uri)) {
|
||||
ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
|
||||
int bufferSize = 1024;
|
||||
byte[] buffer = new byte[bufferSize];
|
||||
int len = 0;
|
||||
while ((len = is.read(buffer)) != -1) {
|
||||
byteBuffer.write(buffer, 0, len);
|
||||
}
|
||||
byte[] bytes = byteBuffer.toByteArray();
|
||||
saveFiles(bytes);
|
||||
// AlertManager.error(""+(bytes.length));
|
||||
} catch (IOException e) {
|
||||
// Handle the exception
|
||||
}
|
||||
}
|
||||
});
|
||||
b.startActivityForResult(intent, 1);
|
||||
}
|
||||
|
||||
|
||||
private static void saveFiles(byte[] data){
|
||||
BuiltByteParser bbp = new BuiltByteParser(data);
|
||||
try{
|
||||
List<BuiltByteParser.parsedObject> parsedObjectList = bbp.parse();
|
||||
|
||||
ArrayList<String> filenames = new ArrayList<>();
|
||||
|
||||
for(int i = 0; i < parsedObjectList.size(); i++){
|
||||
BuiltByteParser.parsedObject pa = parsedObjectList.get(i);
|
||||
if(pa.getType() != file.typecode) continue;
|
||||
file f = file.decode((byte[]) pa.get());
|
||||
if(f == null) continue;
|
||||
filenames.add(f.filename);
|
||||
fileEditor.writeFile(f.filename, f.data);
|
||||
}
|
||||
|
||||
AlertManager.alert("Saved",
|
||||
String.join("\n", filenames));
|
||||
|
||||
}catch (BuiltByteParser.byteParsingExeption e){
|
||||
AlertManager.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,12 +34,14 @@ public class TransferFragment extends Fragment {
|
||||
private static final int background_color = 0x5000ff00;
|
||||
private static final int unselected_background_color = 0x2000ff00;
|
||||
|
||||
// private Bundle b;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
|
||||
// b = savedInstanceState;
|
||||
|
||||
binding = FragmentTransferBinding.inflate(inflater, container, false);
|
||||
|
||||
@@ -114,19 +116,21 @@ public class TransferFragment extends Fragment {
|
||||
|
||||
private void start_upload() {
|
||||
FileSelectorFragment.setOnSelect(data -> {
|
||||
CodeGeneratorView.setData(data);
|
||||
BluetoothSenderFragment.set_data(data);
|
||||
TransferSelectorFragment.setOnSelect(new TransferSelectorFragment.onSelect() {
|
||||
@Override
|
||||
public void onSelectCodes(TransferSelectorFragment self) {
|
||||
CodeGeneratorView.setData(data);
|
||||
findNavController(self).navigate(R.id.action_navigation_transfer_selector_to_navigation_code_generator);
|
||||
}
|
||||
@Override
|
||||
public void onSelectBluetooth(TransferSelectorFragment self) {
|
||||
BluetoothSenderFragment.set_data(data);
|
||||
findNavController(self).navigate(R.id.action_navigation_transfer_selector_to_navigation_bluetooth_sender);
|
||||
}
|
||||
@Override
|
||||
public void onSelectWifi(TransferSelectorFragment self) {}
|
||||
public void onSelectFileBundle(TransferSelectorFragment self) {
|
||||
FileBundle.send(data, getContext());
|
||||
}
|
||||
});
|
||||
findNavController(this).navigate(R.id.action_navigation_file_selector_to_navigation_transfer_selector);
|
||||
});
|
||||
@@ -137,6 +141,7 @@ public class TransferFragment extends Fragment {
|
||||
|
||||
|
||||
private void start_download(){
|
||||
|
||||
TransferSelectorFragment.setOnSelect(new TransferSelectorFragment.onSelect() {
|
||||
@Override
|
||||
public void onSelectCodes(TransferSelectorFragment self) {
|
||||
@@ -149,7 +154,9 @@ public class TransferFragment extends Fragment {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectWifi(TransferSelectorFragment self) {}
|
||||
public void onSelectFileBundle(TransferSelectorFragment self) {
|
||||
FileBundle.receive(getActivity());
|
||||
}
|
||||
});
|
||||
findNavController(this).navigate(R.id.action_navigation_transfer_to_navigation_transfer_selector);
|
||||
}
|
||||
|
||||
+4
-4
@@ -14,7 +14,7 @@ import com.astatin3.scoutingapp2025.databinding.FragmentTransferSelectorBinding;
|
||||
public class TransferSelectorFragment extends Fragment {
|
||||
|
||||
// Declaring three blank funcs in one line lol
|
||||
private static onSelect onselect = new onSelect() {@Override public void onSelectCodes(TransferSelectorFragment self) {}@Override public void onSelectBluetooth(TransferSelectorFragment self) {} @Override public void onSelectWifi(TransferSelectorFragment self) {}};
|
||||
private static onSelect onselect = new onSelect() {@Override public void onSelectCodes(TransferSelectorFragment self) {}@Override public void onSelectBluetooth(TransferSelectorFragment self) {} @Override public void onSelectFileBundle(TransferSelectorFragment self) {}};
|
||||
|
||||
public static void setOnSelect(onSelect tmp) {
|
||||
onselect = tmp;
|
||||
@@ -23,7 +23,7 @@ public class TransferSelectorFragment extends Fragment {
|
||||
public interface onSelect {
|
||||
void onSelectCodes(TransferSelectorFragment self);
|
||||
void onSelectBluetooth(TransferSelectorFragment self);
|
||||
void onSelectWifi(TransferSelectorFragment self);
|
||||
void onSelectFileBundle(TransferSelectorFragment self);
|
||||
}
|
||||
|
||||
FragmentTransferSelectorBinding binding;
|
||||
@@ -38,8 +38,8 @@ public class TransferSelectorFragment extends Fragment {
|
||||
onselect.onSelectBluetooth(this);
|
||||
});
|
||||
|
||||
binding.wifiButton.setOnClickListener(view -> {
|
||||
onselect.onSelectWifi(this);
|
||||
binding.fileBundleButton.setOnClickListener(view -> {
|
||||
onselect.onSelectFileBundle(this);
|
||||
});
|
||||
|
||||
return binding.getRoot();
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.astatin3.scoutingapp2025.utility;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class SharePrompt {
|
||||
public static void shareContent(Context context, String fileName, String content, String mimeType) {
|
||||
shareContent(context, fileName, content.getBytes(), mimeType);
|
||||
}
|
||||
|
||||
public static void shareContent(Context context, String fileName, byte[] content, String mimeType) {
|
||||
try {
|
||||
File file = new File(context.getCacheDir(), fileName);
|
||||
FileOutputStream fos = new FileOutputStream(file);
|
||||
fos.write(content);
|
||||
fos.close();
|
||||
|
||||
Uri fileUri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", file);
|
||||
|
||||
Intent shareIntent = new Intent(Intent.ACTION_SEND);
|
||||
shareIntent.setType(mimeType);
|
||||
shareIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
|
||||
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
|
||||
context.startActivity(Intent.createChooser(shareIntent, "Share using"));
|
||||
} catch (IOException e) {
|
||||
AlertManager.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -228,7 +228,10 @@ public final class fileEditor {
|
||||
}
|
||||
|
||||
public static byte[] readFile(String path){
|
||||
File file = new File(baseDir + path);
|
||||
return readFileExact(baseDir + path);
|
||||
}
|
||||
public static byte[] readFileExact(String path){
|
||||
File file = new File(path);
|
||||
int size = (int) file.length();
|
||||
byte[] bytes = new byte[size];
|
||||
try {
|
||||
|
||||
@@ -33,28 +33,28 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Upload"
|
||||
android:textSize="34sp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/downloadButton"
|
||||
app:layout_constraintBottom_toTopOf="@+id/CSVButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/CSVButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="CSV"
|
||||
android:textSize="34sp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/downloadButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/uploadButton" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/downloadButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Download"
|
||||
android:textSize="34sp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/CSVButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/uploadButton" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/CSVButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="CSV"
|
||||
android:textSize="34sp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/TBAButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -69,7 +69,7 @@
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/downloadButton" />
|
||||
app:layout_constraintTop_toBottomOf="@+id/CSVButton" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
@@ -21,16 +21,16 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Bluetooth"
|
||||
android:textSize="34sp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/wifiButton"
|
||||
app:layout_constraintBottom_toTopOf="@+id/fileBundleButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/codes_button" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/wifiButton"
|
||||
android:id="@+id/fileBundleButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="TODO"
|
||||
android:text="File Bundle"
|
||||
android:textSize="34sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
||||
Reference in New Issue
Block a user