mirror of
https://github.com/Team4388/RidgeScout.git
synced 2026-06-09 08:38:03 -06:00
Compare commits
9 Commits
TODO
...
test-branch
| Author | SHA1 | Date | |
|---|---|---|---|
| b41ed2667c | |||
| 5279c085e1 | |||
| 41460fcd7e | |||
| 782fb73050 | |||
| 7e9954d78a | |||
| 65baecac35 | |||
| e278bc10a1 | |||
| 5d727cf359 | |||
| ae147771cb |
@@ -1,3 +1,9 @@
|
|||||||
|
# Python server
|
||||||
|
__pycache__/
|
||||||
|
metadata.json
|
||||||
|
api_key.txt
|
||||||
|
server_data/
|
||||||
|
|
||||||
# Gradle files
|
# Gradle files
|
||||||
.gradle/
|
.gradle/
|
||||||
build/
|
build/
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
### TODO:
|
|
||||||
##### Scouting:
|
|
||||||
- Make an easier way to make game-specific UI elements
|
|
||||||
##### Data Analysis:
|
|
||||||
- Add analysis for the rest of the data types besides Tally
|
|
||||||
- Add a "scout note" system for scouters to contribute to the scouting report?
|
|
||||||
- Make data that has been marked for rescouting not processed in either by team or by type analysis.
|
|
||||||
##### Functionality:
|
|
||||||
- Rewrite FTP transfer to be over HTTP requests
|
|
||||||
- - Potentially using a python server or some other pre-existing file transfer over HTTP
|
|
||||||
- Delete file menu
|
|
||||||
- Make "Sync meta files" option only block uploading fields specifically.
|
|
||||||
##### UI:
|
|
||||||
- Update docs and README.md with new features
|
|
||||||
- Improve file status indicator for scouting
|
|
||||||
- - The autosave timeout can be significantly reduced, so the save indicator is not a necessity
|
|
||||||
- - A new system for the rescout indicator should be used.
|
|
||||||
- Improve UI elements for scouting data by team
|
|
||||||
- Field button in settings overlaps with other elements in some devices
|
|
||||||
|
|
||||||
### In Progress:
|
|
||||||
##### Scouting:
|
|
||||||
##### Data Analysis:
|
|
||||||
##### Functionality:
|
|
||||||
##### UI:
|
|
||||||
|
|
||||||
### Done:
|
|
||||||
##### Scouting:
|
|
||||||
##### Data Analysis:
|
|
||||||
##### Functionality:
|
|
||||||
##### UI:
|
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.ridgebotics.ridgescout.scoutingData;
|
package com.ridgebotics.ridgescout.scoutingData;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import com.ridgebotics.ridgescout.scoutingData.transfer.TransferType;
|
import com.ridgebotics.ridgescout.scoutingData.transfer.TransferType;
|
||||||
import com.ridgebotics.ridgescout.types.ScoutingArray;
|
import com.ridgebotics.ridgescout.types.ScoutingArray;
|
||||||
import com.ridgebotics.ridgescout.types.data.RawDataType;
|
import com.ridgebotics.ridgescout.types.data.RawDataType;
|
||||||
@@ -31,15 +33,15 @@ public class ScoutingDataWriter {
|
|||||||
switch (data[i].getValueType()){
|
switch (data[i].getValueType()){
|
||||||
case NUM:
|
case NUM:
|
||||||
bb.addInt((int) data[i].forceGetValue());
|
bb.addInt((int) data[i].forceGetValue());
|
||||||
System.out.println("Saved INT: " + data[i].getUUID() + ", ("+ data[i].get() +")");
|
Log.i(ScoutingDataWriter.class.toString(),"Saved INT: " + data[i].getUUID() + ", ("+ data[i].get() +")");
|
||||||
break;
|
break;
|
||||||
case STRING:
|
case STRING:
|
||||||
bb.addString((String) data[i].forceGetValue());
|
bb.addString((String) data[i].forceGetValue());
|
||||||
System.out.println("Saved STR: " + data[i].getUUID() + ", ("+ data[i].get() +")");
|
Log.i(ScoutingDataWriter.class.toString(), "Saved STR: " + data[i].getUUID() + ", ("+ data[i].get() +")");
|
||||||
break;
|
break;
|
||||||
case NUMARR:
|
case NUMARR:
|
||||||
bb.addIntArray((int[]) data[i].forceGetValue());
|
bb.addIntArray((int[]) data[i].forceGetValue());
|
||||||
System.out.println("Saved INT Array: " + data[i].getUUID() + ", ("+ Arrays.toString((int[]) data[i].get()) +")");
|
Log.i(ScoutingDataWriter.class.toString(), "Saved INT Array: " + data[i].getUUID() + ", ("+ Arrays.toString((int[]) data[i].get()) +")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
byte[] bytes = bb.build();
|
byte[] bytes = bb.build();
|
||||||
@@ -82,17 +84,17 @@ public class ScoutingDataWriter {
|
|||||||
case 1: // Int
|
case 1: // Int
|
||||||
rawDataTypes[i] = IntType.newNull(values[version][i].UUID);
|
rawDataTypes[i] = IntType.newNull(values[version][i].UUID);
|
||||||
rawDataTypes[i].forceSetValue(objects.get(i+2).get());
|
rawDataTypes[i].forceSetValue(objects.get(i+2).get());
|
||||||
System.out.println("Loaded INT: " + values[version][i].name + " (" + values[version][i].UUID + ") " + ", ("+ rawDataTypes[i].get() +")");
|
Log.i(ParsedScoutingDataResult.class.toString(),"Loaded INT: " + values[version][i].name + " (" + values[version][i].UUID + ") " + ", ("+ rawDataTypes[i].get() +")");
|
||||||
break;
|
break;
|
||||||
case 2: // String
|
case 2: // String
|
||||||
rawDataTypes[i] = StringType.newNull(values[version][i].UUID);
|
rawDataTypes[i] = StringType.newNull(values[version][i].UUID);
|
||||||
rawDataTypes[i].forceSetValue(objects.get(i+2).get());
|
rawDataTypes[i].forceSetValue(objects.get(i+2).get());
|
||||||
System.out.println("Loaded STR: " + values[version][i].name + " (" + values[version][i].UUID + ") " + ", ("+ rawDataTypes[i].get() +")");
|
Log.i(ParsedScoutingDataResult.class.toString(),"Loaded STR: " + values[version][i].name + " (" + values[version][i].UUID + ") " + ", ("+ rawDataTypes[i].get() +")");
|
||||||
break;
|
break;
|
||||||
case 3: // Int array
|
case 3: // Int array
|
||||||
rawDataTypes[i] = IntArrType.newNull(values[version][i].UUID);
|
rawDataTypes[i] = IntArrType.newNull(values[version][i].UUID);
|
||||||
rawDataTypes[i].forceSetValue(objects.get(i+2).get());
|
rawDataTypes[i].forceSetValue(objects.get(i+2).get());
|
||||||
System.out.println("Loaded intARR: " + values[version][i].name + " (" + values[version][i].UUID + ") " + ", ("+ Arrays.toString((int[]) rawDataTypes[i].get()) +")");
|
Log.i(ParsedScoutingDataResult.class.toString(),"Loaded intARR: " + values[version][i].name + " (" + values[version][i].UUID + ") " + ", ("+ Arrays.toString((int[]) rawDataTypes[i].get()) +")");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.ridgebotics.ridgescout.types;
|
package com.ridgebotics.ridgescout.types;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import com.ridgebotics.ridgescout.scoutingData.transfer.CreateTransferType;
|
import com.ridgebotics.ridgescout.scoutingData.transfer.CreateTransferType;
|
||||||
import com.ridgebotics.ridgescout.scoutingData.transfer.DirectTransferType;
|
import com.ridgebotics.ridgescout.scoutingData.transfer.DirectTransferType;
|
||||||
import com.ridgebotics.ridgescout.scoutingData.transfer.TransferType;
|
import com.ridgebotics.ridgescout.scoutingData.transfer.TransferType;
|
||||||
@@ -44,7 +46,7 @@ public class ScoutingArray {
|
|||||||
}
|
}
|
||||||
this.array = new_values;
|
this.array = new_values;
|
||||||
version++;
|
version++;
|
||||||
System.out.println("Updated to " + version);
|
Log.i(getClass().toString(),"Updated to " + version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ public abstract class FieldType {
|
|||||||
public static final int numberType = 251;
|
public static final int numberType = 251;
|
||||||
public static final int checkboxType = 250;
|
public static final int checkboxType = 250;
|
||||||
public static final int fieldposType = 249;
|
public static final int fieldposType = 249;
|
||||||
|
public static final int toggleType = 248;
|
||||||
|
|
||||||
public enum inputTypes {
|
public enum inputTypes {
|
||||||
SLIDER,
|
SLIDER,
|
||||||
@@ -32,7 +33,8 @@ public abstract class FieldType {
|
|||||||
TALLY,
|
TALLY,
|
||||||
NUMBER,
|
NUMBER,
|
||||||
CHECKBOX,
|
CHECKBOX,
|
||||||
FIELDPOS
|
FIELDPOS,
|
||||||
|
TOGGLE;
|
||||||
}
|
}
|
||||||
public String UUID;
|
public String UUID;
|
||||||
public String name;
|
public String name;
|
||||||
@@ -89,8 +91,6 @@ public abstract class FieldType {
|
|||||||
public abstract void setViewValue(Object value);
|
public abstract void setViewValue(Object value);
|
||||||
public abstract RawDataType getViewValue();
|
public abstract RawDataType getViewValue();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public abstract void add_individual_view(LinearLayout parent, RawDataType data);
|
public abstract void add_individual_view(LinearLayout parent, RawDataType data);
|
||||||
public abstract void add_compiled_view(LinearLayout parent, RawDataType[] data);
|
public abstract void add_compiled_view(LinearLayout parent, RawDataType[] data);
|
||||||
public abstract void add_history_view(LinearLayout parent, RawDataType[] data);
|
public abstract void add_history_view(LinearLayout parent, RawDataType[] data);
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ public class TallyType extends FieldType {
|
|||||||
|
|
||||||
public TallyCounterView tally = null;
|
public TallyCounterView tally = null;
|
||||||
|
|
||||||
|
|
||||||
public View createView(Context context, Function<RawDataType, Integer> onUpdate){
|
public View createView(Context context, Function<RawDataType, Integer> onUpdate){
|
||||||
tally = new TallyCounterView(context);
|
tally = new TallyCounterView(context);
|
||||||
tally.setOnCountChangedListener(n -> onUpdate.apply(getViewValue()));
|
tally.setOnCountChangedListener(n -> onUpdate.apply(getViewValue()));
|
||||||
|
|||||||
@@ -0,0 +1,368 @@
|
|||||||
|
package com.ridgebotics.ridgescout.types.input;
|
||||||
|
|
||||||
|
import static com.ridgebotics.ridgescout.utility.Colors.chart_background;
|
||||||
|
import static com.ridgebotics.ridgescout.utility.Colors.chart_text;
|
||||||
|
import static com.ridgebotics.ridgescout.utility.Colors.dropdown_value_text_1;
|
||||||
|
import static com.ridgebotics.ridgescout.utility.Colors.dropdown_value_text_2;
|
||||||
|
import static com.ridgebotics.ridgescout.utility.Colors.tally_data;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TableLayout;
|
||||||
|
import android.widget.TableRow;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.github.mikephil.charting.charts.LineChart;
|
||||||
|
import com.github.mikephil.charting.components.Legend;
|
||||||
|
import com.github.mikephil.charting.data.Entry;
|
||||||
|
import com.github.mikephil.charting.data.LineData;
|
||||||
|
import com.github.mikephil.charting.data.LineDataSet;
|
||||||
|
import com.ridgebotics.ridgescout.types.data.IntType;
|
||||||
|
import com.ridgebotics.ridgescout.types.data.RawDataType;
|
||||||
|
import com.ridgebotics.ridgescout.ui.data.DataProcessing;
|
||||||
|
import com.ridgebotics.ridgescout.ui.views.CandlestickHeader;
|
||||||
|
import com.ridgebotics.ridgescout.ui.views.CandlestickView;
|
||||||
|
import com.ridgebotics.ridgescout.ui.views.TallyCounterView;
|
||||||
|
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
|
||||||
|
import com.ridgebotics.ridgescout.utility.ByteBuilder;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public class ToggleType extends FieldType {
|
||||||
|
public int get_byte_id() {return toggleType;}
|
||||||
|
public inputTypes getInputType(){return inputTypes.TOGGLE;}
|
||||||
|
public RawDataType.valueTypes getValueType(){return RawDataType.valueTypes.NUM;}
|
||||||
|
public Object get_fallback_value(){return 0;}
|
||||||
|
public ToggleType(){}
|
||||||
|
public String get_type_name(){return "Toggle";}
|
||||||
|
public ToggleType(String UUID, String name, String description, int default_value){
|
||||||
|
super(UUID, name, description);
|
||||||
|
this.default_value = default_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void encodeData(ByteBuilder bb) throws ByteBuilder.buildingException {
|
||||||
|
bb.addInt((int) default_value);
|
||||||
|
}
|
||||||
|
public void decodeData(ArrayList<BuiltByteParser.parsedObject> objects) {
|
||||||
|
default_value = objects.get(0).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public TallyCounterView tally = null;
|
||||||
|
|
||||||
|
|
||||||
|
public View createView(Context context, Function<RawDataType, Integer> onUpdate){
|
||||||
|
tally = new TallyCounterView(context);
|
||||||
|
tally.setOnCountChangedListener(n -> onUpdate.apply(getViewValue()));
|
||||||
|
|
||||||
|
setViewValue(default_value);
|
||||||
|
|
||||||
|
return tally;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setViewValue(Object value) {
|
||||||
|
if(tally == null) return;
|
||||||
|
if(IntType.isNull((int)value)){
|
||||||
|
nullify();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isBlank = false;
|
||||||
|
tally.setVisibility(View.VISIBLE);
|
||||||
|
tally.setValue((int) value);
|
||||||
|
}
|
||||||
|
public void nullify(){
|
||||||
|
isBlank = true;
|
||||||
|
tally.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
public RawDataType getViewValue(){
|
||||||
|
if(tally == null) return null;
|
||||||
|
if(tally.getVisibility() == View.GONE) return IntType.newNull(name);
|
||||||
|
return new IntType(name, tally.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void add_individual_view(LinearLayout parent, RawDataType data){
|
||||||
|
if(data.isNull()) return;
|
||||||
|
|
||||||
|
TextView tv = new TextView(parent.getContext());
|
||||||
|
tv.setLayoutParams(new FrameLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
));
|
||||||
|
tv.setGravity(Gravity.CENTER_HORIZONTAL);
|
||||||
|
tv.setText(String.valueOf((int) data.get()));
|
||||||
|
tv.setTextSize(24);
|
||||||
|
parent.addView(tv);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static float calculateMean(int[] data) {
|
||||||
|
float sum = 0;
|
||||||
|
for (int value : data) {
|
||||||
|
sum += (float) value;
|
||||||
|
}
|
||||||
|
return sum / data.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float calculateStandardDeviation(int[] data, float mean) {
|
||||||
|
float sum = 0;
|
||||||
|
for (int value : data) {
|
||||||
|
sum += (float) Math.pow((float) value - mean, 2);
|
||||||
|
}
|
||||||
|
return (float) Math.sqrt(sum / (data.length - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Entry> generateNormalDistribution(float mean, float stdDev, int count, int scale) {
|
||||||
|
List<Entry> entries = new ArrayList<>();
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
float y = (float) ((1 / (stdDev * Math.sqrt(2 * Math.PI)))
|
||||||
|
* Math.exp(-0.5 * Math.pow(((float) i - mean) / stdDev, 2)));
|
||||||
|
entries.add(new Entry((float) i, y*scale)); // Scale y for visibility
|
||||||
|
}
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int findMin(RawDataType[] data){
|
||||||
|
int min = (int)data[0].get();
|
||||||
|
for(int i = 1; i < data.length; i++)
|
||||||
|
if((int)data[i].get() < min)
|
||||||
|
min = (int)data[i].get();
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int findMax(RawDataType[] data){
|
||||||
|
int max = (int)data[0].get();
|
||||||
|
for(int i = 1; i < data.length; i++)
|
||||||
|
if((int)data[i].get() > max)
|
||||||
|
max = (int)data[i].get();
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add_compiled_view(LinearLayout parent, RawDataType[] data){
|
||||||
|
LineChart chart = new LineChart(parent.getContext());
|
||||||
|
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
);
|
||||||
|
layout.height = 350;
|
||||||
|
chart.setLayoutParams(layout);
|
||||||
|
chart.setBackgroundColor(0xff252025);
|
||||||
|
|
||||||
|
int min = findMin(data);
|
||||||
|
int max = findMax(data);
|
||||||
|
|
||||||
|
int[] values = new int[max-min+1];
|
||||||
|
|
||||||
|
for (int i = 0; i < data.length; i++)
|
||||||
|
if(data[i] != null && data[i].isNull())
|
||||||
|
values[(int) data[i].get()-min]++;
|
||||||
|
|
||||||
|
|
||||||
|
ArrayList<Integer> mean_temp = new ArrayList<>();
|
||||||
|
for (int i = 0; i < data.length; i++)
|
||||||
|
if((int)data[i].get() != 0)
|
||||||
|
mean_temp.add((int) data[i].get());
|
||||||
|
|
||||||
|
int[] mean_vals = mean_temp.stream().mapToInt(Integer::intValue).toArray();
|
||||||
|
|
||||||
|
List<Entry> entries = new ArrayList<>();
|
||||||
|
for (int i = 0; i < values.length; i++)
|
||||||
|
entries.add(new Entry(i, values[i]));
|
||||||
|
|
||||||
|
|
||||||
|
LineDataSet dataSet = new LineDataSet(entries, name);
|
||||||
|
dataSet.setColor(tally_data);
|
||||||
|
dataSet.setValueTextColor(dropdown_value_text_1);
|
||||||
|
dataSet.setDrawCircles(false);
|
||||||
|
dataSet.setDrawValues(false);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Calculate mean and standard deviation
|
||||||
|
float mean = calculateMean(mean_vals);
|
||||||
|
float stdDev = calculateStandardDeviation(mean_vals, mean);
|
||||||
|
|
||||||
|
// Generate normal distribution curve
|
||||||
|
List<Entry> normalDistEntries = generateNormalDistribution(mean-min, stdDev, max-min+1, (max-min)/data.length);
|
||||||
|
|
||||||
|
|
||||||
|
LineDataSet normalDistSet = new LineDataSet(normalDistEntries, "Normal Distribution");
|
||||||
|
normalDistSet.setColor(dropdown_value_text_2);
|
||||||
|
normalDistSet.setDrawCircles(false);
|
||||||
|
normalDistSet.setDrawValues(false);
|
||||||
|
normalDistSet.setLineWidth(2f);
|
||||||
|
|
||||||
|
LineData lineData = new LineData(dataSet, normalDistSet);
|
||||||
|
|
||||||
|
chart.setData(lineData);
|
||||||
|
chart.invalidate();
|
||||||
|
|
||||||
|
chart.getDescription().setEnabled(false);
|
||||||
|
chart.setTouchEnabled(false);
|
||||||
|
chart.setDragEnabled(false);
|
||||||
|
chart.setScaleEnabled(false);
|
||||||
|
|
||||||
|
dataSet.setValueTextColor(dropdown_value_text_2);
|
||||||
|
|
||||||
|
chart.getXAxis().setTextColor(chart_text);
|
||||||
|
chart.getAxisLeft().setTextColor(chart_text);
|
||||||
|
chart.getAxisRight().setTextColor(chart_text);
|
||||||
|
|
||||||
|
Legend legend = chart.getLegend();
|
||||||
|
legend.setTextColor(chart_text);
|
||||||
|
|
||||||
|
parent.addView(chart);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void add_history_view(LinearLayout parent, RawDataType[] data){
|
||||||
|
LineChart chart = new LineChart(parent.getContext());
|
||||||
|
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
);
|
||||||
|
layout.height = 350;
|
||||||
|
chart.setLayoutParams(layout);
|
||||||
|
chart.setBackgroundColor(chart_background);
|
||||||
|
|
||||||
|
int min = findMin(data);
|
||||||
|
int max = findMax(data);
|
||||||
|
|
||||||
|
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()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LineDataSet dataSet = new LineDataSet(entries, name);
|
||||||
|
dataSet.setColor(tally_data);
|
||||||
|
dataSet.setValueTextColor(dropdown_value_text_1);
|
||||||
|
dataSet.setDrawCircles(false);
|
||||||
|
dataSet.setDrawValues(false);
|
||||||
|
|
||||||
|
LineData lineData = new LineData(dataSet);
|
||||||
|
|
||||||
|
chart.setData(lineData);
|
||||||
|
chart.invalidate();
|
||||||
|
|
||||||
|
chart.getDescription().setEnabled(false);
|
||||||
|
chart.setTouchEnabled(false);
|
||||||
|
chart.setDragEnabled(false);
|
||||||
|
chart.setScaleEnabled(false);
|
||||||
|
|
||||||
|
dataSet.setValueTextColor(dropdown_value_text_2);
|
||||||
|
|
||||||
|
chart.getXAxis().setTextColor(chart_text);
|
||||||
|
chart.getAxisLeft().setTextColor(chart_text);
|
||||||
|
chart.getAxisRight().setTextColor(chart_text);
|
||||||
|
|
||||||
|
Legend legend = chart.getLegend();
|
||||||
|
legend.setTextColor(chart_text);
|
||||||
|
|
||||||
|
|
||||||
|
chart.getAxisLeft().setAxisMinimum(min);
|
||||||
|
chart.getAxisLeft().setAxisMaximum(max);
|
||||||
|
|
||||||
|
chart.getAxisRight().setAxisMinimum(min);
|
||||||
|
chart.getAxisRight().setAxisMaximum(max);
|
||||||
|
|
||||||
|
|
||||||
|
parent.addView(chart);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addDataToTable(TableLayout parent, Map<Integer, List<RawDataType>> data){
|
||||||
|
int[] tmp_abs_bounds = DataProcessing.getNumberBounds(data);
|
||||||
|
int absmin = tmp_abs_bounds[0];
|
||||||
|
int absmax = tmp_abs_bounds[1];
|
||||||
|
|
||||||
|
//(int[]) teamData.get(i).get())[0];
|
||||||
|
// AlertManager.alert("Results","Min: " + min + " Max: " + max);
|
||||||
|
|
||||||
|
parent.removeAllViews();
|
||||||
|
|
||||||
|
List<CandlestickView> views = new ArrayList<>();
|
||||||
|
|
||||||
|
for(Integer teamNum : data.keySet()){
|
||||||
|
CandlestickView candlestickView = new CandlestickView(parent.getContext());
|
||||||
|
candlestickView.fromTeamData(data.get(teamNum), teamNum, absmin, absmax);
|
||||||
|
views.add(candlestickView);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TableRow row = new TableRow(parent.getContext());
|
||||||
|
|
||||||
|
// Make candlestick chart fill full width
|
||||||
|
parent.setColumnStretchable(1, true);
|
||||||
|
|
||||||
|
// Fill in top left cell
|
||||||
|
row.addView(new View(parent.getContext()));
|
||||||
|
|
||||||
|
CandlestickHeader header = new CandlestickHeader(parent.getContext());
|
||||||
|
header.setScale(absmin, absmax);
|
||||||
|
row.addView(header);
|
||||||
|
|
||||||
|
parent.addView(row);
|
||||||
|
|
||||||
|
// parent.addView(new );
|
||||||
|
|
||||||
|
try {
|
||||||
|
Collections.sort(views, (a, b) -> (int) ((b.average - a.average) * 50.f));
|
||||||
|
}catch (Exception e){}
|
||||||
|
|
||||||
|
for(int i = 0; i < views.size(); i++){
|
||||||
|
row = new TableRow(parent.getContext());
|
||||||
|
CandlestickView view = views.get(i);
|
||||||
|
|
||||||
|
TextView teamNum = new TextView(parent.getContext());
|
||||||
|
TableRow.LayoutParams params = new TableRow.LayoutParams();
|
||||||
|
params.gravity = Gravity.CENTER;
|
||||||
|
teamNum.setLayoutParams(params);
|
||||||
|
teamNum.setPadding(10,10,10,10);
|
||||||
|
teamNum.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Headline6);
|
||||||
|
teamNum.setText(String.valueOf(view.teamNum));
|
||||||
|
|
||||||
|
|
||||||
|
row.addView(teamNum);
|
||||||
|
row.addView(view);
|
||||||
|
|
||||||
|
parent.addView(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString(RawDataType data){
|
||||||
|
return String.valueOf((int) data.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -10,6 +10,7 @@ import static com.ridgebotics.ridgescout.utility.DataManager.rescout_list;
|
|||||||
|
|
||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -63,7 +64,7 @@ public class FieldDataFragment extends Fragment {
|
|||||||
|
|
||||||
for (int i = 0; i < filenames.size(); i++) {
|
for (int i = 0; i < filenames.size(); i++) {
|
||||||
try {
|
try {
|
||||||
System.out.println("Loading: " + filenames.get(i));
|
Log.i(getClass().toString(), "Loading: " + filenames.get(i));
|
||||||
ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(filenames.get(i), match_values, match_transferValues);
|
ScoutingDataWriter.ParsedScoutingDataResult psda = ScoutingDataWriter.load(filenames.get(i), match_values, match_transferValues);
|
||||||
if (psda.data.array[fieldIndex] != null && psda.data.array[fieldIndex].get() != null && !psda.data.array[fieldIndex].isNull())
|
if (psda.data.array[fieldIndex] != null && psda.data.array[fieldIndex].get() != null && !psda.data.array[fieldIndex].isNull())
|
||||||
teamData.add(psda.data.array[fieldIndex]);
|
teamData.add(psda.data.array[fieldIndex]);
|
||||||
@@ -75,7 +76,7 @@ public class FieldDataFragment extends Fragment {
|
|||||||
data.put(teamNum, teamData);
|
data.put(teamNum, teamData);
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println("Finished!");
|
Log.i(getClass().toString(), "Finished!");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import static com.ridgebotics.ridgescout.utility.DataManager.event;
|
|||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -276,7 +277,7 @@ public class EventFragment extends Fragment {
|
|||||||
builder.setPositiveButton("OK", (dialogInterface, i) -> {
|
builder.setPositiveButton("OK", (dialogInterface, i) -> {
|
||||||
|
|
||||||
int index = dropdown.getIndex();
|
int index = dropdown.getIndex();
|
||||||
System.out.println(index);
|
Log.i(getClass().toString(), String.valueOf(index));
|
||||||
if(!(index >= 0 && index < teamNums.size())) return;
|
if(!(index >= 0 && index < teamNums.size())) return;
|
||||||
|
|
||||||
event.teams.remove(index);
|
event.teams.remove(index);
|
||||||
|
|||||||
+20
-18
@@ -9,6 +9,7 @@ import static com.ridgebotics.ridgescout.utility.DataManager.event;
|
|||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -48,8 +49,9 @@ public class MatchScoutingFragment extends Fragment {
|
|||||||
alliance_position = SettingsManager.getAllyPos();
|
alliance_position = SettingsManager.getAllyPos();
|
||||||
username = SettingsManager.getUsername();
|
username = SettingsManager.getUsername();
|
||||||
|
|
||||||
binding.username.setText(username);
|
binding.bindicator.setUsername(username);
|
||||||
binding.alliancePosText.setText(alliance_position);
|
binding.bindicator.setAlliancePos(alliance_position);
|
||||||
|
binding.bindicator.bringToFront();
|
||||||
|
|
||||||
binding.matchTeamCard.setVisibility(View.GONE);
|
binding.matchTeamCard.setVisibility(View.GONE);
|
||||||
clear_fields();
|
clear_fields();
|
||||||
@@ -65,7 +67,7 @@ public class MatchScoutingFragment extends Fragment {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
binding.nextButton.setOnClickListener(v -> {
|
binding.bindicator.match_indicator_next_button.setOnClickListener(v -> {
|
||||||
if(edited) save();
|
if(edited) save();
|
||||||
SettingsManager.setMatchNum(cur_match_num+1);
|
SettingsManager.setMatchNum(cur_match_num+1);
|
||||||
cur_match_num += 1;
|
cur_match_num += 1;
|
||||||
@@ -74,21 +76,21 @@ public class MatchScoutingFragment extends Fragment {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if(SettingsManager.getEnableQuickAlliancePosChange())
|
if(SettingsManager.getEnableQuickAlliancePosChange())
|
||||||
binding.fileIndicator.setOnClickListener(v -> {
|
binding.bindicator.setOnClickListener(v -> {
|
||||||
// if(e.getAction() != MotionEvent.ACTION_MOVE) return true;
|
// if(e.getAction() != MotionEvent.ACTION_MOVE) return true;
|
||||||
// System.out.println(e.getAxisValue(0));
|
// System.out.println(e.getAxisValue(0));
|
||||||
if(edited) save();
|
if(edited) save();
|
||||||
|
|
||||||
alliance_position = incrementMatchPos(alliance_position);
|
alliance_position = incrementMatchPos(alliance_position);
|
||||||
SettingsManager.setAllyPos(alliance_position);
|
SettingsManager.setAllyPos(alliance_position);
|
||||||
binding.alliancePosText.setText(alliance_position);
|
binding.bindicator.setAlliancePos(alliance_position);
|
||||||
|
|
||||||
update_match_num();
|
update_match_num();
|
||||||
update_scouting_data();
|
update_scouting_data();
|
||||||
// return true;
|
// return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
binding.backButton.setOnClickListener(v -> {
|
binding.bindicator.match_indicator_back_button.setOnClickListener(v -> {
|
||||||
if(edited) save();
|
if(edited) save();
|
||||||
SettingsManager.setMatchNum(cur_match_num-1);
|
SettingsManager.setMatchNum(cur_match_num-1);
|
||||||
cur_match_num -= 1;
|
cur_match_num -= 1;
|
||||||
@@ -150,7 +152,7 @@ public class MatchScoutingFragment extends Fragment {
|
|||||||
|
|
||||||
|
|
||||||
public void save(){
|
public void save(){
|
||||||
System.out.println("Saved!");
|
Log.i(this.getClass().toString(), "Saved!");
|
||||||
edited = false;
|
edited = false;
|
||||||
enableRescoutButton();
|
enableRescoutButton();
|
||||||
AlertManager.toast("Saved " + filename);
|
AlertManager.toast("Saved " + filename);
|
||||||
@@ -158,7 +160,7 @@ public class MatchScoutingFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void set_indicator_color(int color){
|
public void set_indicator_color(int color){
|
||||||
binding.fileIndicator.setBackgroundColor(color);
|
binding.bindicator.setColor(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update_asm(){
|
public void update_asm(){
|
||||||
@@ -238,18 +240,18 @@ public class MatchScoutingFragment extends Fragment {
|
|||||||
|
|
||||||
edited = false;
|
edited = false;
|
||||||
|
|
||||||
binding.matchnum.setText(String.valueOf(cur_match_num+1));
|
binding.bindicator.setMatchNum(String.valueOf(cur_match_num+1));
|
||||||
|
|
||||||
if(cur_match_num <= 0){
|
if(cur_match_num <= 0){
|
||||||
binding.backButton.setVisibility(View.GONE);
|
binding.bindicator.match_indicator_back_button.setVisibility(View.GONE);
|
||||||
}else{
|
}else{
|
||||||
binding.backButton.setVisibility(View.VISIBLE);
|
binding.bindicator.match_indicator_back_button.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(cur_match_num >= event.matches.size()-1){
|
if(cur_match_num >= event.matches.size()-1){
|
||||||
binding.nextButton.setVisibility(View.GONE);
|
binding.bindicator.match_indicator_next_button.setVisibility(View.GONE);
|
||||||
}else{
|
}else{
|
||||||
binding.nextButton.setVisibility(View.VISIBLE);
|
binding.bindicator.match_indicator_next_button.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,7 +273,7 @@ public class MatchScoutingFragment extends Fragment {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.barTeamNum.setText(String.valueOf(team_num));
|
binding.bindicator.setTeamNum(String.valueOf(team_num));
|
||||||
|
|
||||||
frcTeam team = null;
|
frcTeam team = null;
|
||||||
for(int i=0; i < event.teams.size(); i++){
|
for(int i=0; i < event.teams.size(); i++){
|
||||||
@@ -377,14 +379,14 @@ public class MatchScoutingFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(ScoutingDataWriter.save(DataManager.match_values.length-1, ScoutingDataWriter.checkAddName(fileUsernames, username), filename, types))
|
if(ScoutingDataWriter.save(DataManager.match_values.length-1, ScoutingDataWriter.checkAddName(fileUsernames, username), filename, types))
|
||||||
System.out.println("Saved!");
|
Log.i(getClass().toString(), "Saved!");
|
||||||
else
|
else
|
||||||
System.out.println("Error saving");
|
Log.i(getClass().toString(), "Error saving");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void enableRescoutButton(){
|
private void enableRescoutButton(){
|
||||||
set_indicator_color(rescout ? rescout_color : saved_color);
|
set_indicator_color(rescout ? rescout_color : saved_color);
|
||||||
binding.fileIndicator.setOnLongClickListener(v -> {
|
binding.bindicator.setOnLongClickListener(v -> {
|
||||||
rescout = !rescout;
|
rescout = !rescout;
|
||||||
if(rescout){
|
if(rescout){
|
||||||
set_indicator_color(rescout_color);
|
set_indicator_color(rescout_color);
|
||||||
@@ -401,6 +403,6 @@ public class MatchScoutingFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void disableRescoutButton(){
|
private void disableRescoutButton(){
|
||||||
binding.fileIndicator.setOnLongClickListener(null);
|
binding.bindicator.setOnLongClickListener(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import static com.ridgebotics.ridgescout.utility.DataManager.pit_transferValues;
|
|||||||
import static com.ridgebotics.ridgescout.utility.DataManager.pit_values;
|
import static com.ridgebotics.ridgescout.utility.DataManager.pit_values;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -91,10 +92,10 @@ public class PitScoutingFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(ScoutingDataWriter.save(pit_values.length-1, ScoutingDataWriter.checkAddName(fileUsernames, username), filename, types)) {
|
if(ScoutingDataWriter.save(pit_values.length-1, ScoutingDataWriter.checkAddName(fileUsernames, username), filename, types)) {
|
||||||
System.out.println("Saved!");
|
Log.i(getClass().toString(), "Saved!");
|
||||||
AlertManager.toast("Saved " + filename);
|
Log.i(getClass().toString(), "Saved " + filename);
|
||||||
}else
|
}else
|
||||||
System.out.println("Error saving");
|
Log.i(getClass().toString(), "Error saving");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void set_indicator_color(int color){
|
public void set_indicator_color(int color){
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import androidx.fragment.app.Fragment;
|
|||||||
|
|
||||||
import com.ridgebotics.ridgescout.R;
|
import com.ridgebotics.ridgescout.R;
|
||||||
import com.ridgebotics.ridgescout.types.frcEvent;
|
import com.ridgebotics.ridgescout.types.frcEvent;
|
||||||
|
import com.ridgebotics.ridgescout.utility.AlertManager;
|
||||||
import com.ridgebotics.ridgescout.utility.FileEditor;
|
import com.ridgebotics.ridgescout.utility.FileEditor;
|
||||||
import com.ridgebotics.ridgescout.utility.SettingsManager;
|
import com.ridgebotics.ridgescout.utility.SettingsManager;
|
||||||
import com.ridgebotics.ridgescout.databinding.FragmentScoutingBinding;
|
import com.ridgebotics.ridgescout.databinding.FragmentScoutingBinding;
|
||||||
@@ -126,7 +127,12 @@ public class ScoutingFragment extends Fragment {
|
|||||||
binding.textName.setText("Welcome, " + SettingsManager.getUsername() + "!");
|
binding.textName.setText("Welcome, " + SettingsManager.getUsername() + "!");
|
||||||
|
|
||||||
int matchNum = SettingsManager.getMatchNum();
|
int matchNum = SettingsManager.getMatchNum();
|
||||||
int nextMatch = event.getNextTeamMatch(SettingsManager.getTeamNum(), matchNum).matchIndex;
|
int nextMatch = -1;
|
||||||
|
try {
|
||||||
|
nextMatch = event.getNextTeamMatch(SettingsManager.getTeamNum(), matchNum).matchIndex;
|
||||||
|
} catch (Exception e){
|
||||||
|
AlertManager.error(e);
|
||||||
|
}
|
||||||
|
|
||||||
binding.textNextMatch.setText("Our next match: Match " + nextMatch);
|
binding.textNextMatch.setText("Our next match: Match " + nextMatch);
|
||||||
binding.textMatchAlliance.setText("Match: " + (matchNum+1) + ", " + SettingsManager.getAllyPos());
|
binding.textMatchAlliance.setText("Match: " + (matchNum+1) + ", " + SettingsManager.getAllyPos());
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import com.ridgebotics.ridgescout.types.input.NumberType;
|
|||||||
import com.ridgebotics.ridgescout.types.input.SliderType;
|
import com.ridgebotics.ridgescout.types.input.SliderType;
|
||||||
import com.ridgebotics.ridgescout.types.input.TallyType;
|
import com.ridgebotics.ridgescout.types.input.TallyType;
|
||||||
import com.ridgebotics.ridgescout.types.input.TextType;
|
import com.ridgebotics.ridgescout.types.input.TextType;
|
||||||
|
import com.ridgebotics.ridgescout.types.input.ToggleType;
|
||||||
import com.ridgebotics.ridgescout.utility.AlertManager;
|
import com.ridgebotics.ridgescout.utility.AlertManager;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
@@ -29,7 +30,8 @@ public class FieldEditorHelper {
|
|||||||
paramNumber,
|
paramNumber,
|
||||||
paramString,
|
paramString,
|
||||||
paramStringArray,
|
paramStringArray,
|
||||||
paramNumberArray
|
paramNumberArray,
|
||||||
|
paramBoolean
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class parameterType {
|
public static class parameterType {
|
||||||
@@ -64,6 +66,15 @@ public class FieldEditorHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class paramBoolean extends parameterType{
|
||||||
|
public boolean val;
|
||||||
|
public paramBoolean(String name, boolean val){
|
||||||
|
this.name = name + " (Boolean)";
|
||||||
|
this.val = val;
|
||||||
|
this.id = parameterTypeEnum.paramBoolean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// public static class paramNumberArray extends parameterType{
|
// public static class paramNumberArray extends parameterType{
|
||||||
// public int[] val;
|
// public int[] val;
|
||||||
// public paramNumberArray(String name, int[] val){
|
// public paramNumberArray(String name, int[] val){
|
||||||
@@ -113,6 +124,12 @@ public class FieldEditorHelper {
|
|||||||
new paramNumber("Default Y", 0)
|
new paramNumber("Default Y", 0)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static final parameterType[] defaultToggleParam = new parameterType[]{
|
||||||
|
new paramString("Name", "New Toggle"),
|
||||||
|
new paramString("Description", ""),
|
||||||
|
new paramBoolean("Default true or false",true)
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
private static parameterType[] getSliderParams(SliderType s){
|
private static parameterType[] getSliderParams(SliderType s){
|
||||||
return new parameterType[]{
|
return new parameterType[]{
|
||||||
@@ -173,6 +190,13 @@ public class FieldEditorHelper {
|
|||||||
new paramNumber("Default Y", ((int[]) s.default_value)[1])
|
new paramNumber("Default Y", ((int[]) s.default_value)[1])
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
private static parameterType[] getToggleParams(ToggleType s){
|
||||||
|
return new parameterType[]{
|
||||||
|
new paramString("Name", s.name),
|
||||||
|
new paramString("Description", s.description),
|
||||||
|
new paramBoolean("Default true or false", (boolean) s.default_value)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -224,6 +248,12 @@ public class FieldEditorHelper {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void setToggleParam(ToggleType s, parameterType[] types){
|
||||||
|
s.name = ((paramString) types[0]).val;
|
||||||
|
s.description = ((paramString) types[1]).val;
|
||||||
|
s.default_value = ((paramBoolean) types[2]).val;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private static void setInputParameter(FieldType t, parameterType[] types){
|
private static void setInputParameter(FieldType t, parameterType[] types){
|
||||||
switch (t.getInputType()){
|
switch (t.getInputType()){
|
||||||
@@ -248,6 +278,9 @@ public class FieldEditorHelper {
|
|||||||
case FIELDPOS:
|
case FIELDPOS:
|
||||||
setFieldPosParam((FieldposType) t, types);
|
setFieldPosParam((FieldposType) t, types);
|
||||||
break;
|
break;
|
||||||
|
case TOGGLE:
|
||||||
|
setToggleParam((ToggleType) t, types);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,6 +302,8 @@ public class FieldEditorHelper {
|
|||||||
return getCheckboxParam((CheckboxType) t);
|
return getCheckboxParam((CheckboxType) t);
|
||||||
case FIELDPOS:
|
case FIELDPOS:
|
||||||
return getFieldPosParam((FieldposType) t);
|
return getFieldPosParam((FieldposType) t);
|
||||||
|
case TOGGLE:
|
||||||
|
return getToggleParams((ToggleType) t);
|
||||||
}
|
}
|
||||||
return new parameterType[]{};
|
return new parameterType[]{};
|
||||||
}
|
}
|
||||||
@@ -305,6 +340,16 @@ public class FieldEditorHelper {
|
|||||||
));
|
));
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
private static View createBooleanEdit(Context c, boolean value){
|
||||||
|
EditText text = new EditText(c);
|
||||||
|
text.setText(value);
|
||||||
|
text.setLayoutParams(new LinearLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
));
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private static View createEdit(Context c, parameterType t){
|
private static View createEdit(Context c, parameterType t){
|
||||||
switch (t.id){
|
switch (t.id){
|
||||||
@@ -384,6 +429,11 @@ public class FieldEditorHelper {
|
|||||||
fieldposType.UUID = UUID.randomUUID().toString();
|
fieldposType.UUID = UUID.randomUUID().toString();
|
||||||
setFieldPosParam(fieldposType, defaultFieldPosParam);
|
setFieldPosParam(fieldposType, defaultFieldPosParam);
|
||||||
return fieldposType;
|
return fieldposType;
|
||||||
|
case 7:
|
||||||
|
ToggleType toggleType = new ToggleType();
|
||||||
|
toggleType.UUID = UUID.randomUUID().toString();
|
||||||
|
setToggleParam(toggleType, defaultToggleParam);
|
||||||
|
return toggleType;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import android.annotation.SuppressLint;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -270,7 +271,7 @@ public class FieldsFragment extends Fragment {
|
|||||||
|
|
||||||
System.arraycopy(currentValues, 0, newValues, 0, currentValues.length);
|
System.arraycopy(currentValues, 0, newValues, 0, currentValues.length);
|
||||||
|
|
||||||
System.out.println("Length: " + values.size());
|
Log.i(getClass().toString(), "Length: " + values.size());
|
||||||
|
|
||||||
newValues[currentValues.length] = new FieldType[values.size()];
|
newValues[currentValues.length] = new FieldType[values.size()];
|
||||||
for(int i = 0; i < values.size(); i++) {
|
for(int i = 0; i < values.size(); i++) {
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ import com.ridgebotics.ridgescout.ui.views.CustomSpinnerView;
|
|||||||
import com.ridgebotics.ridgescout.ui.views.TallyCounterView;
|
import com.ridgebotics.ridgescout.ui.views.TallyCounterView;
|
||||||
import com.ridgebotics.ridgescout.utility.DataManager;
|
import com.ridgebotics.ridgescout.utility.DataManager;
|
||||||
import com.ridgebotics.ridgescout.utility.FileEditor;
|
import com.ridgebotics.ridgescout.utility.FileEditor;
|
||||||
|
import com.ridgebotics.ridgescout.utility.SettingsManager;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -92,11 +93,13 @@ public class SettingsFragment extends Fragment {
|
|||||||
|
|
||||||
manager.addItem(new CheckboxSettingsItem(CustomEventsKey, "Custom Events"));
|
manager.addItem(new CheckboxSettingsItem(CustomEventsKey, "Custom Events"));
|
||||||
|
|
||||||
StringSettingsItem FTPServer = new StringSettingsItem(com.ridgebotics.ridgescout.utility.SettingsManager.FTPServer, "FTP Server (Sync)");
|
StringSettingsItem FTPKey = new StringSettingsItem(com.ridgebotics.ridgescout.utility.SettingsManager.FTPKey, "Sync Key");
|
||||||
|
manager.addItem(FTPKey);
|
||||||
|
StringSettingsItem FTPServer = new StringSettingsItem(com.ridgebotics.ridgescout.utility.SettingsManager.FTPServer, "Sync Server (Sync)");
|
||||||
manager.addItem(FTPServer);
|
manager.addItem(FTPServer);
|
||||||
CheckboxSettingsItem FTPSendMetaFiles = new CheckboxSettingsItem(com.ridgebotics.ridgescout.utility.SettingsManager.FTPSendMetaFiles, "Sync meta files");
|
CheckboxSettingsItem FTPSendMetaFiles = new CheckboxSettingsItem(com.ridgebotics.ridgescout.utility.SettingsManager.FTPSendMetaFiles, "⚠ Send meta files");
|
||||||
manager.addItem(FTPSendMetaFiles);
|
manager.addItem(FTPSendMetaFiles);
|
||||||
CheckboxSettingsItem FTPEnabled = new CheckboxSettingsItem(com.ridgebotics.ridgescout.utility.SettingsManager.FTPEnabled, "FTP Enabled", FTPServer, FTPSendMetaFiles);
|
CheckboxSettingsItem FTPEnabled = new CheckboxSettingsItem(com.ridgebotics.ridgescout.utility.SettingsManager.FTPEnabled, "FTP Enabled", FTPServer, FTPKey, FTPSendMetaFiles);
|
||||||
manager.addItem(FTPEnabled);
|
manager.addItem(FTPEnabled);
|
||||||
|
|
||||||
manager.addItem(new CheckboxSettingsItem(WifiModeKey, "Wifi Mode", FTPEnabled));
|
manager.addItem(new CheckboxSettingsItem(WifiModeKey, "Wifi Mode", FTPEnabled));
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package com.ridgebotics.ridgescout.ui.transfer;
|
|||||||
import static com.ridgebotics.ridgescout.utility.DataManager.evcode;
|
import static com.ridgebotics.ridgescout.utility.DataManager.evcode;
|
||||||
import static com.ridgebotics.ridgescout.utility.FileEditor.baseDir;
|
import static com.ridgebotics.ridgescout.utility.FileEditor.baseDir;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import com.ridgebotics.ridgescout.utility.AlertManager;
|
import com.ridgebotics.ridgescout.utility.AlertManager;
|
||||||
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
|
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
|
||||||
import com.ridgebotics.ridgescout.utility.ByteBuilder;
|
import com.ridgebotics.ridgescout.utility.ByteBuilder;
|
||||||
@@ -26,6 +28,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
// This is now deprecated
|
||||||
// Class to synchronise data over FTP.
|
// Class to synchronise data over FTP.
|
||||||
public class FTPSync extends Thread {
|
public class FTPSync extends Thread {
|
||||||
public static final String remoteBasePath = "/RidgeScout/";
|
public static final String remoteBasePath = "/RidgeScout/";
|
||||||
@@ -156,13 +159,13 @@ public class FTPSync extends Thread {
|
|||||||
|
|
||||||
if (remoteTimestamp == null || after(localTimeStamp, remoteTimestamp)) {
|
if (remoteTimestamp == null || after(localTimeStamp, remoteTimestamp)) {
|
||||||
uploadFile(localFile);
|
uploadFile(localFile);
|
||||||
System.out.println("Uploaded" + localFile.getName());
|
Log.i(getClass().toString(), "Uploaded" + localFile.getName());
|
||||||
|
|
||||||
setLocalFileTimestamp(localFile, curSyncTime);
|
setLocalFileTimestamp(localFile, curSyncTime);
|
||||||
remoteTimestamps.put(localFile.getName(), curSyncTime);
|
remoteTimestamps.put(localFile.getName(), curSyncTime);
|
||||||
upCount++;
|
upCount++;
|
||||||
}else{
|
}else{
|
||||||
System.out.println("Did not upload");
|
Log.i(getClass().toString(), "Did not upload");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -191,17 +194,17 @@ public class FTPSync extends Thread {
|
|||||||
if (!localFile.exists() || (after(remoteTimestamp, localTimeStamp) && !localTimeStamp.equals(remoteTimestamp))) {
|
if (!localFile.exists() || (after(remoteTimestamp, localTimeStamp) && !localTimeStamp.equals(remoteTimestamp))) {
|
||||||
downloadFile(remoteFile, localFile);
|
downloadFile(remoteFile, localFile);
|
||||||
|
|
||||||
System.out.println("Downloaded " + localFile.getName());
|
Log.i(getClass().toString(), "Downloaded " + localFile.getName());
|
||||||
|
|
||||||
if(!localFile.exists()) System.out.println("Not exist");
|
if(!localFile.exists()) Log.i(getClass().toString(), "Not exist");
|
||||||
else if(after(remoteTimestamp, localTimeStamp)) System.out.println("Before: " + (localTimeStamp.getTime()-remoteTimestamp.getTime()));
|
else if(after(remoteTimestamp, localTimeStamp)) Log.i(getClass().toString(), "Before: " + (localTimeStamp.getTime()-remoteTimestamp.getTime()));
|
||||||
|
|
||||||
// Date d = getUtcTimestamp(remoteFile);
|
// Date d = getUtcTimestamp(remoteFile);
|
||||||
setLocalFileTimestamp(localFile, remoteTimestamps.get(localFile.getName()));
|
setLocalFileTimestamp(localFile, remoteTimestamps.get(localFile.getName()));
|
||||||
// remoteTimestamps.put(remoteFile, curSyncTime);
|
// remoteTimestamps.put(remoteFile, curSyncTime);
|
||||||
downCount++;
|
downCount++;
|
||||||
}else{
|
}else{
|
||||||
System.out.println("Did not download");
|
Log.i(getClass().toString(), "Did not download");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,369 @@
|
|||||||
|
package com.ridgebotics.ridgescout.ui.transfer;
|
||||||
|
|
||||||
|
import static com.ridgebotics.ridgescout.utility.FileEditor.baseDir;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.ridgebotics.ridgescout.utility.AlertManager;
|
||||||
|
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
|
||||||
|
import com.ridgebotics.ridgescout.utility.ByteBuilder;
|
||||||
|
import com.ridgebotics.ridgescout.utility.FileEditor;
|
||||||
|
import com.ridgebotics.ridgescout.utility.HttpGetFile;
|
||||||
|
import com.ridgebotics.ridgescout.utility.HttpPutFile;
|
||||||
|
import com.ridgebotics.ridgescout.utility.RequestTask;
|
||||||
|
import com.ridgebotics.ridgescout.utility.SettingsManager;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
// This is now deprsicated
|
||||||
|
// Class to syncronise data over FTP.
|
||||||
|
public class HttpSync extends Thread {
|
||||||
|
public static final String timestampsFilename = "timestamps";
|
||||||
|
|
||||||
|
private static final long millisTolerance = 1000;
|
||||||
|
|
||||||
|
private boolean after(Date a, Date b){
|
||||||
|
return a.getTime() - b.getTime() > millisTolerance;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public interface onResult {
|
||||||
|
void onResult(boolean error, int upCount, int downCount);
|
||||||
|
}
|
||||||
|
public interface UpdateIndicator {
|
||||||
|
void onText(String text);
|
||||||
|
}
|
||||||
|
private static UpdateIndicator updateIndicator = text -> {};
|
||||||
|
public static String text = "";
|
||||||
|
private static void setUpdateIndicator(String m_text){
|
||||||
|
text = m_text;
|
||||||
|
updateIndicator.onText(m_text);
|
||||||
|
}
|
||||||
|
public static void setOnUpdateIndicator(UpdateIndicator m_updateIndicator){
|
||||||
|
updateIndicator = m_updateIndicator;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static onResult onResult = (error, upCount, downCount) -> {};
|
||||||
|
public static void setOnResult(onResult result){
|
||||||
|
onResult = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isRunning = false;
|
||||||
|
public static boolean getIsRunning(){return isRunning;}
|
||||||
|
|
||||||
|
public static void sync(){
|
||||||
|
// DataManager.reload_event();
|
||||||
|
HttpSync sync = new HttpSync();
|
||||||
|
|
||||||
|
sync.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int upCount = 0;
|
||||||
|
private int downCount = 0;
|
||||||
|
|
||||||
|
private class TransferFile {
|
||||||
|
public String filename;
|
||||||
|
public Date updated;
|
||||||
|
public String checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<TransferFile> localFiles = new ArrayList<>();
|
||||||
|
private List<TransferFile> remoteFiles = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void await() {
|
||||||
|
while(!runningRequest.get()) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(100);
|
||||||
|
} catch (InterruptedException e) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AtomicBoolean runningRequest = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
isRunning = true;
|
||||||
|
boolean sendMetaFiles = SettingsManager.getFTPSendMetaFiles();
|
||||||
|
String serverIP = SettingsManager.getFTPServer();
|
||||||
|
String serverKey = SettingsManager.getFTPKey();
|
||||||
|
|
||||||
|
setUpdateIndicator("Getting Metadata...");
|
||||||
|
|
||||||
|
// Load metadata from server
|
||||||
|
getRemoteFileMetadata(serverIP, serverKey);
|
||||||
|
|
||||||
|
if(!isRunning){
|
||||||
|
setUpdateIndicator("Error Connecting");
|
||||||
|
onResult.onResult(true, upCount, downCount);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getLocalFileMetadata();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Wait for metadata request to finish
|
||||||
|
|
||||||
|
setUpdateIndicator("Uploading 0%");
|
||||||
|
|
||||||
|
for(int i = 0; i < localFiles.size(); i++){
|
||||||
|
TransferFile localFile = localFiles.get(i);
|
||||||
|
|
||||||
|
TransferFile remoteFile = findInFileArray(remoteFiles, localFile.filename);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(
|
||||||
|
(
|
||||||
|
sendMetaFiles || !(
|
||||||
|
localFile.filename.endsWith(".fields")
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
&& (remoteFile == null ||
|
||||||
|
(
|
||||||
|
!Objects.equals(localFile.checksum, remoteFile.checksum) &&
|
||||||
|
after(localFile.updated, remoteFile.updated)
|
||||||
|
)
|
||||||
|
)) {
|
||||||
|
uploadFile(localFile, serverIP, serverKey);
|
||||||
|
// await();
|
||||||
|
Log.d(getClass().toString(), "LocalFile: " + localFile.filename + ", " + localFile.checksum + ", " + localFile.updated + ": Uploaded");
|
||||||
|
upCount++;
|
||||||
|
}else {
|
||||||
|
Log.d(getClass().toString(), "LocalFile: " + localFile.filename + ", " + localFile.checksum + ", " + localFile.updated + ": Not uploaded");
|
||||||
|
}
|
||||||
|
|
||||||
|
setUpdateIndicator("Uploading " + (Math.floor((double) (i * 1000) / localFiles.size()) / 10) + "%");
|
||||||
|
}
|
||||||
|
|
||||||
|
setUpdateIndicator("Downloading 0%");
|
||||||
|
|
||||||
|
for(int i = 0; i < remoteFiles.size(); i++){
|
||||||
|
TransferFile remoteFile = remoteFiles.get(i);
|
||||||
|
|
||||||
|
TransferFile localFile = findInFileArray(localFiles, remoteFile.filename);
|
||||||
|
|
||||||
|
if(localFile == null ||
|
||||||
|
(
|
||||||
|
!Objects.equals(localFile.checksum, remoteFile.checksum) &&
|
||||||
|
after(remoteFile.updated, localFile.updated) &&
|
||||||
|
!localFile.updated.equals(remoteFile.updated)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
downloadFile(remoteFile, serverIP);
|
||||||
|
// await();
|
||||||
|
Log.d(getClass().toString(), "RemoteFile: " + remoteFile.filename + ", " + remoteFile.checksum + ", " + remoteFile.updated + ": Downloaded");
|
||||||
|
downCount++;
|
||||||
|
} else {
|
||||||
|
Log.d(getClass().toString(), "RemoteFile: " + remoteFile.filename + ", " + remoteFile.checksum + ", " + remoteFile.updated + ": Not downloaded");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
setUpdateIndicator("Downloading " + (Math.floor((double) (i * 1000) / remoteFiles.size()) / 10) + "%");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
setUpdateIndicator("Finished, " + upCount + " Up, " + downCount + " Down");
|
||||||
|
|
||||||
|
|
||||||
|
onResult.onResult(false, upCount, downCount);
|
||||||
|
isRunning = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private TransferFile findInFileArray(List<TransferFile> files, String filename){
|
||||||
|
for(TransferFile file : files) {
|
||||||
|
if(file.filename.equals(filename))
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Date getLocalFileUtcTimestamp(File file) {
|
||||||
|
return new Date(file.lastModified());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getSHA256Hash(String filePath) throws IOException, NoSuchAlgorithmException {
|
||||||
|
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||||
|
FileInputStream fis = new FileInputStream(filePath);
|
||||||
|
byte[] byteArray = new byte[1024];
|
||||||
|
int bytesCount = 0;
|
||||||
|
|
||||||
|
while ((bytesCount = fis.read(byteArray)) != -1) {
|
||||||
|
digest.update(byteArray, 0, bytesCount);
|
||||||
|
}
|
||||||
|
fis.close();
|
||||||
|
|
||||||
|
byte[] bytes = digest.digest();
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (byte b : bytes) {
|
||||||
|
sb.append(String.format("%02x", b));
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getLocalFileMetadata() {
|
||||||
|
File localDir = new File(baseDir);
|
||||||
|
File[] localFileNames = localDir.listFiles();
|
||||||
|
|
||||||
|
assert localFileNames != null;
|
||||||
|
for (int i = 0; i < localFileNames.length; i++) {
|
||||||
|
File file = localFileNames[i];
|
||||||
|
|
||||||
|
if(file.isDirectory()) continue;
|
||||||
|
// Remove timestamts file
|
||||||
|
if(file.getName().equals(timestampsFilename)) continue;
|
||||||
|
|
||||||
|
TransferFile tf = new TransferFile();
|
||||||
|
tf.filename = file.getName();
|
||||||
|
tf.updated = getLocalFileUtcTimestamp(file);
|
||||||
|
try {
|
||||||
|
tf.checksum = getSHA256Hash(file.getPath());
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
localFiles.add(tf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send request to server and retrieve metadata
|
||||||
|
private void getRemoteFileMetadata(String serverURL, String serverKey) {
|
||||||
|
final RequestTask rq = new RequestTask();
|
||||||
|
runningRequest.set(false);
|
||||||
|
rq.onResult(metadata -> {
|
||||||
|
try {
|
||||||
|
JSONObject j = new JSONObject(metadata);
|
||||||
|
for (Iterator<String> it = j.keys(); it.hasNext(); ) {
|
||||||
|
String key = it.next();
|
||||||
|
|
||||||
|
JSONObject obj = j.getJSONObject(key);
|
||||||
|
|
||||||
|
TransferFile tf = new TransferFile();
|
||||||
|
tf.filename = key;
|
||||||
|
tf.updated = new Date(Long.parseLong(obj.getString("modified")));
|
||||||
|
tf.checksum = obj.getString("sha256");
|
||||||
|
|
||||||
|
remoteFiles.add(tf);
|
||||||
|
}
|
||||||
|
}catch(JSONException | NullPointerException e ) {
|
||||||
|
AlertManager.error(e);
|
||||||
|
isRunning = false;
|
||||||
|
}
|
||||||
|
runningRequest.set(true);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
rq.execute((serverURL + "/api/metadata"), "api_key: " + serverKey);
|
||||||
|
await();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// private boolean setTimestamps(Map<String, Date> timestamps){
|
||||||
|
// try {
|
||||||
|
// ByteBuilder bb = new ByteBuilder();
|
||||||
|
// String[] filenames = timestamps.keySet().toArray(new String[0]);
|
||||||
|
//
|
||||||
|
// for(int i = 0; i < filenames.length; i++){
|
||||||
|
// bb.addString(filenames[i]);
|
||||||
|
// bb.addLong(timestamps.get(filenames[i]).getTime());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// FileEditor.writeFile(timestampsFilename, bb.build());
|
||||||
|
//
|
||||||
|
// uploadFile(new File(baseDir + timestampsFilename));
|
||||||
|
// return true;
|
||||||
|
// } catch (ByteBuilder.buildingException | IOException e) {
|
||||||
|
// AlertManager.error("Failed Syncing!", e);
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private Map<String, Date> getTimestamps() {
|
||||||
|
// try {
|
||||||
|
// downloadFile(timestampsFilename, new File(baseDir + timestampsFilename));
|
||||||
|
//
|
||||||
|
// byte[] data = FileEditor.readFile(timestampsFilename);
|
||||||
|
//
|
||||||
|
// if(data == null || data.length == 0)
|
||||||
|
// return new HashMap<>();
|
||||||
|
//
|
||||||
|
// BuiltByteParser bbp = new BuiltByteParser(data);
|
||||||
|
// List<BuiltByteParser.parsedObject> pa = bbp.parse();
|
||||||
|
//
|
||||||
|
// Map<String, Date> output = new HashMap<>();
|
||||||
|
// for(int i = 0; i < pa.size(); i+=2){
|
||||||
|
//// System.out.println((long) pa.get(i).get());
|
||||||
|
// output.put(
|
||||||
|
// (String) pa.get(i).get(),
|
||||||
|
// new Date((long) pa.get(i+1).get())
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// return output;
|
||||||
|
//
|
||||||
|
// }catch (IOException | BuiltByteParser.byteParsingExeption e){
|
||||||
|
// AlertManager.error("Failed Syncing!", e);
|
||||||
|
// return new HashMap<>();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
void uploadFile(TransferFile tf, String serverURL, String apiKey) {
|
||||||
|
runningRequest.set(false);
|
||||||
|
HttpPutFile uploadTask = new HttpPutFile(serverURL + "/api/" + tf.filename, new File(baseDir + tf.filename), new HttpPutFile.UploadCallback() {
|
||||||
|
@Override
|
||||||
|
public void onResult(String error) {
|
||||||
|
if(error != null)
|
||||||
|
AlertManager.error(error);
|
||||||
|
runningRequest.set(true);
|
||||||
|
}
|
||||||
|
}, new String[]{
|
||||||
|
"api_key: " + apiKey,
|
||||||
|
("modified: " + tf.updated.getTime())
|
||||||
|
}); // Pass auth token if needed
|
||||||
|
|
||||||
|
uploadTask.execute();
|
||||||
|
await();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void setLocalFileTimestamp(File file, Date date) {
|
||||||
|
file.setLastModified(date.getTime());
|
||||||
|
}
|
||||||
|
void downloadFile(TransferFile tf, String serverURL) {
|
||||||
|
runningRequest.set(false);
|
||||||
|
File f = new File(baseDir + tf.filename);
|
||||||
|
HttpGetFile uploadTask = new HttpGetFile(serverURL + "/api/" + tf.filename, f, new HttpGetFile.DownloadCallback() {
|
||||||
|
@Override
|
||||||
|
public void onResult(String error) {
|
||||||
|
if(error != null)
|
||||||
|
AlertManager.error(error);
|
||||||
|
else
|
||||||
|
setLocalFileTimestamp(f, tf.updated);
|
||||||
|
runningRequest.set(true);
|
||||||
|
|
||||||
|
}
|
||||||
|
}); // Pass auth token if needed
|
||||||
|
|
||||||
|
uploadTask.execute();
|
||||||
|
await();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -65,13 +65,13 @@ public class TransferFragment extends Fragment {
|
|||||||
|
|
||||||
binding.SyncButton.setOnClickListener(v -> {
|
binding.SyncButton.setOnClickListener(v -> {
|
||||||
binding.SyncButton.setEnabled(false);
|
binding.SyncButton.setEnabled(false);
|
||||||
FTPSync.sync();
|
HttpSync.sync();
|
||||||
});
|
});
|
||||||
|
|
||||||
if(FTPSync.getIsRunning())
|
if(HttpSync.getIsRunning())
|
||||||
binding.SyncButton.setEnabled(false);
|
binding.SyncButton.setEnabled(false);
|
||||||
|
|
||||||
FTPSync.setOnResult((error, upcount, downcount) -> {
|
HttpSync.setOnResult((error, upcount, downcount) -> {
|
||||||
if (getActivity() != null)
|
if (getActivity() != null)
|
||||||
getActivity().runOnUiThread(() -> {
|
getActivity().runOnUiThread(() -> {
|
||||||
binding.SyncButton.setEnabled(true);
|
binding.SyncButton.setEnabled(true);
|
||||||
@@ -79,8 +79,8 @@ public class TransferFragment extends Fragment {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
binding.syncIndicator.setText(FTPSync.text);
|
binding.syncIndicator.setText(HttpSync.text);
|
||||||
FTPSync.setOnUpdateIndicator(text -> {if(getActivity() != null) getActivity().runOnUiThread(() -> binding.syncIndicator.setText(text));});
|
HttpSync.setOnUpdateIndicator(text -> {if(getActivity() != null) getActivity().runOnUiThread(() -> binding.syncIndicator.setText(text));});
|
||||||
|
|
||||||
if(evcode.equals("unset")){
|
if(evcode.equals("unset")){
|
||||||
binding.noEventError.setVisibility(View.VISIBLE);
|
binding.noEventError.setVisibility(View.VISIBLE);
|
||||||
|
|||||||
+2
-1
@@ -8,6 +8,7 @@ import android.bluetooth.BluetoothSocket;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.core.app.ActivityCompat;
|
import androidx.core.app.ActivityCompat;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
@@ -139,7 +140,7 @@ public class BluetoothReceiver {
|
|||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (e.getMessage() != null && e.getMessage().contains("bt socket closed, read return: -1")) {
|
if (e.getMessage() != null && e.getMessage().contains("bt socket closed, read return: -1")) {
|
||||||
receiveddata.onConnectionStop();
|
receiveddata.onConnectionStop();
|
||||||
System.out.println("Bluetooth socket closed, treating as end of stream");
|
Log.i(getClass().toString(), "Bluetooth socket closed, treating as end of stream");
|
||||||
} else {
|
} else {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -1,6 +1,7 @@
|
|||||||
package com.ridgebotics.ridgescout.ui.transfer.bluetooth;
|
package com.ridgebotics.ridgescout.ui.transfer.bluetooth;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -117,7 +118,7 @@ public class BluetoothReceiverFragment extends Fragment {
|
|||||||
|
|
||||||
private void receiveData(byte[] data, int bytes) {
|
private void receiveData(byte[] data, int bytes) {
|
||||||
byte[] newBytes = FileEditor.getByteBlock(data, 0, bytes);
|
byte[] newBytes = FileEditor.getByteBlock(data, 0, bytes);
|
||||||
System.out.println("Recieved " + bytes + " Bytes over bluetooth!");
|
Log.i(getClass().toString(), "Recieved " + bytes + " Bytes over bluetooth!");
|
||||||
recievedBytes.add(newBytes);
|
recievedBytes.add(newBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +139,7 @@ public class BluetoothReceiverFragment extends Fragment {
|
|||||||
ScoutingFile f = ScoutingFile.decode((byte[]) result.get(i).get());
|
ScoutingFile f = ScoutingFile.decode((byte[]) result.get(i).get());
|
||||||
|
|
||||||
if (f != null) {
|
if (f != null) {
|
||||||
System.out.println(f.filename);
|
Log.i(getClass().toString(), f.filename);
|
||||||
if (f.write())
|
if (f.write())
|
||||||
result_filenames += f.filename + "\n";
|
result_filenames += f.filename + "\n";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import android.graphics.Canvas;
|
|||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.RectF;
|
import android.graphics.RectF;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import com.ridgebotics.ridgescout.types.data.RawDataType;
|
import com.ridgebotics.ridgescout.types.data.RawDataType;
|
||||||
@@ -208,7 +209,7 @@ public class CandlestickView extends View {
|
|||||||
upperQuartile = DataProcessing.calculatePercentile(teamDataArray, 75);
|
upperQuartile = DataProcessing.calculatePercentile(teamDataArray, 75);
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println(locmin + ", " + lowerQuartile + ", " + avg + ", " + upperQuartile + ", " + locmax);
|
Log.i(getClass().toString(), locmin + ", " + lowerQuartile + ", " + avg + ", " + upperQuartile + ", " + locmax);
|
||||||
setData(locmin, lowerQuartile, avg, upperQuartile, locmax, absmin, absmax);
|
setData(locmin, lowerQuartile, avg, upperQuartile, locmax, absmin, absmax);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package com.ridgebotics.ridgescout.ui.views;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.PorterDuff;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.RelativeLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||||
|
|
||||||
|
import com.ridgebotics.ridgescout.R;
|
||||||
|
import com.ridgebotics.ridgescout.types.frcTeam;
|
||||||
|
|
||||||
|
import org.w3c.dom.Text;
|
||||||
|
|
||||||
|
// A view for displaying information about a team.
|
||||||
|
public class MatchScoutingIndicator extends RelativeLayout {
|
||||||
|
public MatchScoutingIndicator(Context context, @Nullable AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
init(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MatchScoutingIndicator(Context context) {
|
||||||
|
super(context);
|
||||||
|
init(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextView match_indicator_alliance_pos_text;
|
||||||
|
public TextView match_indicator_bar_team_num;
|
||||||
|
public TextView match_indicator_matchnum;
|
||||||
|
public TextView match_indicator_username;
|
||||||
|
public ImageButton match_indicator_back_button;
|
||||||
|
public ImageButton match_indicator_next_button;
|
||||||
|
private ConstraintLayout box;
|
||||||
|
private View coloredBackground;
|
||||||
|
|
||||||
|
public void init(Context context) {
|
||||||
|
LayoutInflater.from(context).inflate(R.layout.view_match_scouting_indicator, this, true);
|
||||||
|
match_indicator_back_button = findViewById(R.id.match_indicator_back_button);
|
||||||
|
match_indicator_next_button = findViewById(R.id.match_indicator_next_button);
|
||||||
|
match_indicator_alliance_pos_text = findViewById(R.id.match_indicator_alliance_pos_text);
|
||||||
|
match_indicator_username = findViewById(R.id.match_indicator_username);
|
||||||
|
match_indicator_matchnum = findViewById(R.id.match_indicator_matchnum);
|
||||||
|
match_indicator_bar_team_num = findViewById(R.id.match_indicator_bar_team_num);
|
||||||
|
box = findViewById(R.id.file_indicator_box);
|
||||||
|
coloredBackground = findViewById(R.id.match_indicator_background);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username){
|
||||||
|
match_indicator_username.setText(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAlliancePos(String alliancePos){
|
||||||
|
match_indicator_alliance_pos_text.setText(alliancePos);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMatchNum(String matchNum){
|
||||||
|
match_indicator_matchnum.setText(matchNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTeamNum(String teamNum){
|
||||||
|
match_indicator_bar_team_num.setText(teamNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setColor(int color){
|
||||||
|
Drawable drawable = box.getBackground();
|
||||||
|
drawable.mutate();
|
||||||
|
drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
|
||||||
|
|
||||||
|
float[] hsv = new float[3];
|
||||||
|
Color.colorToHSV(color,hsv);
|
||||||
|
|
||||||
|
coloredBackground.setBackgroundColor(
|
||||||
|
Color.HSVToColor(220, new float[]{
|
||||||
|
hsv[0],
|
||||||
|
Math.min(hsv[1], 0.75f),
|
||||||
|
Math.min(hsv[2], 0.5f)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
package com.ridgebotics.ridgescout.utility;
|
||||||
|
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
public class HttpGetFile extends AsyncTask<Void, Integer, File> {
|
||||||
|
|
||||||
|
public interface DownloadCallback {
|
||||||
|
void onResult(String error);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String downloadUrl;
|
||||||
|
private File destinationFile;
|
||||||
|
private DownloadCallback callback;
|
||||||
|
private String errorMessage;
|
||||||
|
public HttpGetFile(String downloadUrl, File destinationFile, DownloadCallback callback) {
|
||||||
|
this.downloadUrl = downloadUrl;
|
||||||
|
this.destinationFile = destinationFile;
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected File doInBackground(Void... voids) {
|
||||||
|
HttpURLConnection connection = null;
|
||||||
|
InputStream inputStream = null;
|
||||||
|
FileOutputStream outputStream = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
URL url = new URL(downloadUrl);
|
||||||
|
connection = (HttpURLConnection) url.openConnection();
|
||||||
|
|
||||||
|
// Configure connection for GET request
|
||||||
|
connection.setRequestMethod("GET");
|
||||||
|
connection.setDoInput(true);
|
||||||
|
connection.setConnectTimeout(30000); // 30 seconds
|
||||||
|
connection.setReadTimeout(60000); // 60 seconds
|
||||||
|
|
||||||
|
connection.connect();
|
||||||
|
|
||||||
|
// Check response code
|
||||||
|
int responseCode = connection.getResponseCode();
|
||||||
|
if (responseCode < 200 || responseCode >= 300) {
|
||||||
|
String errorResponse = readErrorResponse(connection);
|
||||||
|
errorMessage = "Download failed. Response code: " + responseCode +
|
||||||
|
(errorResponse != null ? ". Error: " + errorResponse : "");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get file size for progress tracking
|
||||||
|
long fileSize = connection.getContentLengthLong();
|
||||||
|
if (fileSize == -1) {
|
||||||
|
fileSize = connection.getContentLength(); // fallback for older API
|
||||||
|
}
|
||||||
|
|
||||||
|
inputStream = connection.getInputStream();
|
||||||
|
|
||||||
|
// Create destination file and directories if needed
|
||||||
|
if (destinationFile.getParentFile() != null && !destinationFile.getParentFile().exists()) {
|
||||||
|
if (!destinationFile.getParentFile().mkdirs()) {
|
||||||
|
errorMessage = "Failed to create destination directory: " + destinationFile.getParentFile().getAbsolutePath();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outputStream = new FileOutputStream(destinationFile);
|
||||||
|
|
||||||
|
byte[] buffer = new byte[8192];
|
||||||
|
long downloadedBytes = 0;
|
||||||
|
int bytesRead;
|
||||||
|
|
||||||
|
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||||
|
if (isCancelled()) {
|
||||||
|
deletePartialFile();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
outputStream.write(buffer, 0, bytesRead);
|
||||||
|
downloadedBytes += bytesRead;
|
||||||
|
|
||||||
|
// Update progress if file size is known
|
||||||
|
if (fileSize > 0) {
|
||||||
|
int progress = (int) ((downloadedBytes * 100) / fileSize);
|
||||||
|
publishProgress(progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outputStream.flush();
|
||||||
|
// Log.d(TAG, "Download successful. File saved to: " + destinationFile.getAbsolutePath());
|
||||||
|
return destinationFile;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
AlertManager.error(e);
|
||||||
|
errorMessage = "Download error: " + e.getMessage();
|
||||||
|
// Log.e(TAG, errorMessage, e);
|
||||||
|
deletePartialFile();
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
closeResources(inputStream, outputStream, connection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(File result) {
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onResult(errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCancelled() {
|
||||||
|
deletePartialFile();
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onResult("Download cancelled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String readErrorResponse(HttpURLConnection connection) {
|
||||||
|
try {
|
||||||
|
InputStream errorStream = connection.getErrorStream();
|
||||||
|
if (errorStream != null) {
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(errorStream));
|
||||||
|
StringBuilder response = new StringBuilder();
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
response.append(line);
|
||||||
|
}
|
||||||
|
reader.close();
|
||||||
|
return response.toString();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
AlertManager.error(e);
|
||||||
|
// Log.e(TAG, "Error reading error response", e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deletePartialFile() {
|
||||||
|
if (destinationFile != null && destinationFile.exists()) {
|
||||||
|
if (destinationFile.delete()) {
|
||||||
|
// Log.d(TAG, "Partial download file deleted");
|
||||||
|
} else {
|
||||||
|
// Log.w(TAG, "Failed to delete partial download file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void closeResources(InputStream inputStream, OutputStream outputStream, HttpURLConnection connection) {
|
||||||
|
try {
|
||||||
|
if (inputStream != null) inputStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
AlertManager.error(e);
|
||||||
|
// Log.e(TAG, "Error closing input stream", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (outputStream != null) outputStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
AlertManager.error(e);
|
||||||
|
// Log.e(TAG, "Error closing output stream", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connection != null) {
|
||||||
|
connection.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
package com.ridgebotics.ridgescout.utility;
|
||||||
|
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
//import android.util.Log;
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
public class HttpPutFile extends AsyncTask<Void, Integer, Boolean> {
|
||||||
|
|
||||||
|
// private static final String TAG = "FileUploadTask";
|
||||||
|
|
||||||
|
public interface UploadCallback {
|
||||||
|
void onResult(String error);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String uploadUrl;
|
||||||
|
private File fileToUpload;
|
||||||
|
private UploadCallback callback;
|
||||||
|
private String errorMessage;
|
||||||
|
private String[] headers;
|
||||||
|
|
||||||
|
public HttpPutFile(String uploadUrl, File fileToUpload, UploadCallback callback, String[] headers) {
|
||||||
|
this.uploadUrl = uploadUrl;
|
||||||
|
this.fileToUpload = fileToUpload;
|
||||||
|
this.callback = callback;
|
||||||
|
this.headers = headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Boolean doInBackground(Void... voids) {
|
||||||
|
HttpURLConnection connection = null;
|
||||||
|
InputStream fileInputStream = null;
|
||||||
|
OutputStream outputStream = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!fileToUpload.exists()) {
|
||||||
|
errorMessage = "File does not exist: " + fileToUpload.getAbsolutePath();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
URL url = new URL(uploadUrl);
|
||||||
|
connection = (HttpURLConnection) url.openConnection();
|
||||||
|
|
||||||
|
// Configure connection for PUT request
|
||||||
|
connection.setRequestMethod("PUT");
|
||||||
|
connection.setDoOutput(true);
|
||||||
|
connection.setDoInput(true);
|
||||||
|
connection.setUseCaches(false);
|
||||||
|
connection.setRequestProperty("Content-Type", "application/octet-stream");
|
||||||
|
connection.setRequestProperty("Content-Length", String.valueOf(fileToUpload.length()));
|
||||||
|
connection.setConnectTimeout(30000); // 30 seconds
|
||||||
|
connection.setReadTimeout(60000); // 60 seconds
|
||||||
|
|
||||||
|
for(int i = 0; i < headers.length; i++){
|
||||||
|
String[] split = headers[i].split(": ");
|
||||||
|
connection.setRequestProperty(split[0], split[1]);
|
||||||
|
}
|
||||||
|
connection.connect();
|
||||||
|
|
||||||
|
outputStream = connection.getOutputStream();
|
||||||
|
fileInputStream = new FileInputStream(fileToUpload);
|
||||||
|
|
||||||
|
byte[] buffer = new byte[8192];
|
||||||
|
long totalBytes = fileToUpload.length();
|
||||||
|
long uploadedBytes = 0;
|
||||||
|
int bytesRead;
|
||||||
|
|
||||||
|
while ((bytesRead = fileInputStream.read(buffer)) != -1) {
|
||||||
|
if (isCancelled()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
outputStream.write(buffer, 0, bytesRead);
|
||||||
|
uploadedBytes += bytesRead;
|
||||||
|
|
||||||
|
// Update progress
|
||||||
|
int progress = (int) ((uploadedBytes * 100) / totalBytes);
|
||||||
|
publishProgress(progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
outputStream.flush();
|
||||||
|
|
||||||
|
// Check response code
|
||||||
|
int responseCode = connection.getResponseCode();
|
||||||
|
if (responseCode >= 200 && responseCode < 300) {
|
||||||
|
// Log.d(TAG, "Upload successful. Response code: " + responseCode);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// Read error response if available
|
||||||
|
String errorResponse = readErrorResponse(connection);
|
||||||
|
errorMessage = "Upload failed. Response code: " + responseCode +
|
||||||
|
(errorResponse != null ? ". Error: " + errorResponse : "");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
AlertManager.error(e);
|
||||||
|
errorMessage = "Upload error: " + e.getMessage();
|
||||||
|
// Log.e(TAG, errorMessage, e);
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
closeResources(fileInputStream, outputStream, connection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Boolean success) {
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onResult(errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCancelled() {
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onResult("Upload cancelled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String readErrorResponse(HttpURLConnection connection) {
|
||||||
|
try {
|
||||||
|
InputStream errorStream = connection.getErrorStream();
|
||||||
|
if (errorStream != null) {
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(errorStream));
|
||||||
|
StringBuilder response = new StringBuilder();
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
response.append(line);
|
||||||
|
}
|
||||||
|
reader.close();
|
||||||
|
return response.toString();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
AlertManager.error(e);
|
||||||
|
// Log.e(TAG, "Error reading error response", e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void closeResources(InputStream inputStream, OutputStream outputStream, HttpURLConnection connection) {
|
||||||
|
try {
|
||||||
|
if (inputStream != null) inputStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
AlertManager.error(e);
|
||||||
|
// Log.e(TAG, "Error closing input stream", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (outputStream != null) outputStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
AlertManager.error(e);
|
||||||
|
// Log.e(TAG, "Error closing output stream", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connection != null) {
|
||||||
|
connection.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,6 +28,7 @@ public class SettingsManager {
|
|||||||
public static final String BtUUIDKey = "bt_uuid";
|
public static final String BtUUIDKey = "bt_uuid";
|
||||||
public static final String FTPEnabled = "ftp_enabled";
|
public static final String FTPEnabled = "ftp_enabled";
|
||||||
public static final String FTPServer = "ftp_server";
|
public static final String FTPServer = "ftp_server";
|
||||||
|
public static final String FTPKey = "ftp_key";
|
||||||
public static final String FTPSendMetaFiles = "ftp_send_meta_files";
|
public static final String FTPSendMetaFiles = "ftp_send_meta_files";
|
||||||
|
|
||||||
public static final String EnableQuickAllianceChangeKey = "enable_quick_alliance_change";
|
public static final String EnableQuickAllianceChangeKey = "enable_quick_alliance_change";
|
||||||
@@ -53,7 +54,8 @@ public class SettingsManager {
|
|||||||
hm.put(TeamsDataModeKey, 0);
|
hm.put(TeamsDataModeKey, 0);
|
||||||
hm.put(BtUUIDKey, UUID.randomUUID().toString());
|
hm.put(BtUUIDKey, UUID.randomUUID().toString());
|
||||||
hm.put(FTPEnabled, false);
|
hm.put(FTPEnabled, false);
|
||||||
hm.put(FTPServer, "0.0.0.0");
|
hm.put(FTPServer, "http://127.0.0.1:8080");
|
||||||
|
hm.put(FTPKey, "5uper_5ecure_k3y");
|
||||||
hm.put(FTPSendMetaFiles, false);
|
hm.put(FTPSendMetaFiles, false);
|
||||||
hm.put(EnableQuickAllianceChangeKey, false);
|
hm.put(EnableQuickAllianceChangeKey, false);
|
||||||
hm.put(CustomEventsKey, false);
|
hm.put(CustomEventsKey, false);
|
||||||
@@ -131,6 +133,9 @@ public class SettingsManager {
|
|||||||
public static String getFTPServer(){return prefs.getString( FTPServer, (String) defaults.get(FTPServer));}
|
public static String getFTPServer(){return prefs.getString( FTPServer, (String) defaults.get(FTPServer));}
|
||||||
public static void setFTPServer(String str){ getEditor().putString( FTPServer,str).apply();}
|
public static void setFTPServer(String str){ getEditor().putString( FTPServer,str).apply();}
|
||||||
|
|
||||||
|
public static String getFTPKey(){return prefs.getString( FTPKey, (String) defaults.get(FTPKey));}
|
||||||
|
public static void setFTPKey(String str){ getEditor().putString( FTPKey,str).apply();}
|
||||||
|
|
||||||
public static boolean getFTPSendMetaFiles(){return prefs.getBoolean(FTPSendMetaFiles, (boolean) defaults.get(FTPSendMetaFiles));}
|
public static boolean getFTPSendMetaFiles(){return prefs.getBoolean(FTPSendMetaFiles, (boolean) defaults.get(FTPSendMetaFiles));}
|
||||||
public static void setFTPSendMetaFiles(boolean bool){getEditor().putBoolean(FTPSendMetaFiles,bool).apply();}
|
public static void setFTPSendMetaFiles(boolean bool){getEditor().putBoolean(FTPSendMetaFiles,bool).apply();}
|
||||||
|
|
||||||
|
|||||||
Vendored
BIN
Binary file not shown.
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="800dp"
|
||||||
|
android:height="800dp"
|
||||||
|
android:viewportWidth="20"
|
||||||
|
android:viewportHeight="20">
|
||||||
|
<path
|
||||||
|
android:pathData="M14,5v10l-9,-5 9,-5z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="800dp"
|
||||||
|
android:height="800dp"
|
||||||
|
android:viewportWidth="20"
|
||||||
|
android:viewportHeight="20">
|
||||||
|
<path
|
||||||
|
android:pathData="M15,10l-9,5V5l9,5z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
</vector>
|
||||||
@@ -5,16 +5,28 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<com.ridgebotics.ridgescout.ui.views.MatchScoutingIndicator
|
||||||
|
android:id="@+id/bindicator"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="60dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<ScrollView
|
<ScrollView
|
||||||
|
android:id="@+id/scrollView3"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent">
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_bias="0.0">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/MatchScoutArea"
|
android:id="@+id/MatchScoutArea"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:paddingTop="48dp">
|
android:paddingTop="48dp">
|
||||||
|
|
||||||
@@ -25,16 +37,15 @@
|
|||||||
android:layout_alignParentEnd="true"
|
android:layout_alignParentEnd="true"
|
||||||
android:layout_margin="5dp"
|
android:layout_margin="5dp"
|
||||||
android:background="@drawable/border"
|
android:background="@drawable/border"
|
||||||
android:padding="10dp"
|
android:orientation="horizontal"
|
||||||
android:orientation="horizontal">
|
android:padding="10dp">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/scouting_notice_text"
|
android:id="@+id/scouting_notice_text"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
|
android:text="Scouting Notice"
|
||||||
android:text="Scouting Notice">
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"></TextView>
|
||||||
</TextView>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
@@ -46,75 +57,4 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:id="@+id/file_indicator"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="#60ff0000"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/back_button"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Back"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/next_button"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Next"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/alliance_pos_text"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Temp"
|
|
||||||
android:textAlignment="center"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/matchnum"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/back_button"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/username" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/username"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Temp"
|
|
||||||
android:textAlignment="center"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/matchnum"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/back_button"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/matchnum"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Temp"
|
|
||||||
android:textAlignment="center"
|
|
||||||
android:textSize="24sp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/bar_team_num"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Temp"
|
|
||||||
android:textSize="24sp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/next_button"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/matchnum"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/file_indicator_box"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="5dp"
|
||||||
|
android:background="@drawable/border"
|
||||||
|
android:padding="3dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/match_indicator_background"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/match_indicator_back_button"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:src="@drawable/triangle_left"
|
||||||
|
android:text="Back"
|
||||||
|
android:background="@color/zxing_transparent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/match_indicator_next_button"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:src="@drawable/triangle_right"
|
||||||
|
android:text="Next"
|
||||||
|
android:background="@color/zxing_transparent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/match_indicator_alliance_pos_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:text="Temp"
|
||||||
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
|
||||||
|
android:textAlignment="center"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/match_indicator_next_button"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/match_indicator_username"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:text="Temp"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/match_indicator_back_button"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/match_indicator_matchnum"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Temp"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/match_indicator_bar_team_num"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="3dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="Temp"
|
||||||
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/match_indicator_matchnum"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/match_indicator_matchnum"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</RelativeLayout>
|
||||||
+127
@@ -0,0 +1,127 @@
|
|||||||
|
from ast import mod
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import hashlib
|
||||||
|
from datetime import datetime
|
||||||
|
from bottle import Bottle, run, get, put, static_file, response, request,HTTPResponse
|
||||||
|
from random import SystemRandom
|
||||||
|
|
||||||
|
from utils import *
|
||||||
|
|
||||||
|
app = Bottle()
|
||||||
|
|
||||||
|
file_metadata = {}
|
||||||
|
|
||||||
|
def save_metadata():
|
||||||
|
global file_metadata
|
||||||
|
write(METADATA_PATH4, json.dumps(file_metadata))
|
||||||
|
|
||||||
|
def load_metadata():
|
||||||
|
global file_metadata
|
||||||
|
data = read(METADATA_PATH4)
|
||||||
|
if data is not None:
|
||||||
|
file_metadata = json.loads(data)
|
||||||
|
|
||||||
|
api_key = None
|
||||||
|
cryptogen = SystemRandom()
|
||||||
|
|
||||||
|
def aquire_key():
|
||||||
|
global api_key
|
||||||
|
global cryptogen
|
||||||
|
|
||||||
|
if api_key is None:
|
||||||
|
try:
|
||||||
|
api_key = read(API_KEY_PATH).decode("utf-8").strip()
|
||||||
|
except:
|
||||||
|
ran = cryptogen.randrange(10**80)
|
||||||
|
api_key = "%064x" % ran
|
||||||
|
write(API_KEY_PATH, api_key)
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def list_html():
|
||||||
|
global file_metadata
|
||||||
|
load_metadata()
|
||||||
|
|
||||||
|
content = '<html><body><table><tr>'
|
||||||
|
for heading in ['File', 'Size', 'Modified', 'Sha256']:
|
||||||
|
content += f'<th>{heading}</th>'
|
||||||
|
content += "</tr>"
|
||||||
|
|
||||||
|
print(file_metadata)
|
||||||
|
|
||||||
|
for filename in file_metadata.keys():
|
||||||
|
content += "<tr>"
|
||||||
|
content += f'<td><a href="/api/{filename}">{filename}</a></td>'
|
||||||
|
content += f'<td>{file_metadata[filename]["size"]}B</td>'
|
||||||
|
content += f'<td>{file_metadata[filename]["modified"]}</td>'
|
||||||
|
content += f'<td>{file_metadata[filename]["sha256"]}</td>'
|
||||||
|
content += "</tr>"
|
||||||
|
|
||||||
|
content += '</table></body></html>'
|
||||||
|
|
||||||
|
return content
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/metadata')
|
||||||
|
def metadata():
|
||||||
|
global file_metadata
|
||||||
|
load_metadata()
|
||||||
|
response.content_type = 'application/json'
|
||||||
|
return json.dumps(file_metadata)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/<filename>', method='PUT')
|
||||||
|
def upload(filename):
|
||||||
|
global api_key
|
||||||
|
try:
|
||||||
|
sentkey = request.headers[API_KEY_HEADER]
|
||||||
|
if sentkey != api_key:
|
||||||
|
return HTTPResponse(status=403, body=f"Invalid Key")
|
||||||
|
except:
|
||||||
|
return HTTPResponse(status=403, body="You must specify an 'api_key' header")
|
||||||
|
|
||||||
|
|
||||||
|
global file_metadata
|
||||||
|
load_metadata()
|
||||||
|
|
||||||
|
data = request.body.read()
|
||||||
|
|
||||||
|
try:
|
||||||
|
modified = request.headers[MODIFIED_HEADER]
|
||||||
|
except:
|
||||||
|
modified = (datetime.now() - datetime(1970, 1, 1)).total_seconds()
|
||||||
|
|
||||||
|
sha256_hash = hashlib.sha256()
|
||||||
|
sha256_hash.update(data)
|
||||||
|
|
||||||
|
file_metadata[filename] = {
|
||||||
|
'size': len(data),
|
||||||
|
'modified': modified,
|
||||||
|
'sha256': sha256_hash.hexdigest()
|
||||||
|
}
|
||||||
|
|
||||||
|
save_metadata()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
write(os.path.join(DATA_ROOT, filename), data)
|
||||||
|
# save_metadata()
|
||||||
|
# response.content_type = 'application/json'
|
||||||
|
# return json.dumps(file_metadata)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/<filename>')
|
||||||
|
def download(filename):
|
||||||
|
|
||||||
|
data = read(os.path.join(DATA_ROOT, filename))
|
||||||
|
|
||||||
|
if data is not None:
|
||||||
|
response.content_type = 'application/octet-stream'
|
||||||
|
return data
|
||||||
|
else:
|
||||||
|
HTTPResponse(status=404, body="File not found")
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
mkdir(DATA_ROOT)
|
||||||
|
aquire_key()
|
||||||
|
app.run(host='0.0.0.0', port=8080)
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
bottle
|
||||||
|
random
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
ROOT = os.path.dirname(__file__)
|
||||||
|
DATA_ROOT = os.path.join(os.path.dirname(__file__), 'server_data')
|
||||||
|
|
||||||
|
METADATA_PATH4 = os.path.join(ROOT, 'metadata.json')
|
||||||
|
API_KEY_PATH = os.path.join(ROOT, 'api_key.txt')
|
||||||
|
|
||||||
|
MODIFIED_HEADER = 'modified'
|
||||||
|
API_KEY_HEADER = 'api_key'
|
||||||
|
|
||||||
|
def mkdir(path):
|
||||||
|
if not os.path.exists(path):
|
||||||
|
os.makedirs(path)
|
||||||
|
|
||||||
|
def ls(path):
|
||||||
|
try:
|
||||||
|
return os.listdir(path)
|
||||||
|
except:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def read(path):
|
||||||
|
if not os.path.exists(path):
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
with open(path, 'rb') as f:
|
||||||
|
return f.read()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error reading file {path}: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def write(path, data):
|
||||||
|
if not os.path.exists(path):
|
||||||
|
with open(path, mode='ab'): pass
|
||||||
|
|
||||||
|
if isinstance(data, str):
|
||||||
|
data = str.encode(data)
|
||||||
|
|
||||||
|
with open(path, 'wb') as f:
|
||||||
|
f.write(data)
|
||||||
Reference in New Issue
Block a user