mirror of
https://github.com/Team4388/RidgeScout.git
synced 2026-06-09 08:38:03 -06:00
Compare commits
17 Commits
TODO
...
recyclerview
| Author | SHA1 | Date | |
|---|---|---|---|
| 5a644c6671 | |||
| 05a40e39c3 | |||
| 090a0579b9 | |||
| a30664000c | |||
| 1bae273abd | |||
| 5c2b1fef2e | |||
| dbba56e649 | |||
| 4aa31b620d | |||
| 890b879ef9 | |||
| 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/
|
||||
build/
|
||||
|
||||
@@ -28,9 +28,9 @@
|
||||
- Statbotics intgration
|
||||
- Scout error estimation using OPR-like calculation
|
||||
- - Would most likely require Statbotics
|
||||
|
||||
https://www.thebluealliance.com/avatars
|
||||
### Screenshots
|
||||
|Match scouting interface|Field editor|Teams data viewer|
|
||||
|-|-|-|
|
||||
||||
|
||||
|
||||
z
|
||||
@@ -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:
|
||||
@@ -57,19 +57,21 @@ dependencies {
|
||||
implementation(libs.constraintlayout)
|
||||
implementation(libs.lifecycle.livedata.ktx)
|
||||
implementation(libs.lifecycle.viewmodel.ktx)
|
||||
implementation("androidx.navigation:navigation-fragment:2.8.9")
|
||||
implementation(libs.navigation.fragment.v289)
|
||||
implementation(libs.navigation.ui)
|
||||
implementation(libs.preference)
|
||||
// implementation(libs.support.annotations)
|
||||
// implementation(libs.asynclayoutinflator)
|
||||
|
||||
testImplementation(libs.junit)
|
||||
|
||||
androidTestImplementation(libs.ext.junit)
|
||||
androidTestImplementation(libs.espresso.core)
|
||||
|
||||
|
||||
var camerax_version = "1.3.2"
|
||||
implementation("androidx.camera:camera-core:1.3.2")
|
||||
implementation("androidx.camera:camera-camera2:1.3.2")
|
||||
implementation("androidx.camera:camera-lifecycle:1.3.2")
|
||||
var camerax_version = "1.4.2"
|
||||
implementation("androidx.camera:camera-core:${camerax_version}")
|
||||
implementation("androidx.camera:camera-camera2:${camerax_version}")
|
||||
implementation("androidx.camera:camera-lifecycle:${camerax_version}")
|
||||
implementation("androidx.camera:camera-view:${camerax_version}")
|
||||
|
||||
implementation("com.journeyapps:zxing-android-embedded:4.3.0")
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.ridgebotics.ridgescout.scoutingData;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.ridgebotics.ridgescout.scoutingData.transfer.TransferType;
|
||||
import com.ridgebotics.ridgescout.types.ScoutingArray;
|
||||
import com.ridgebotics.ridgescout.types.data.RawDataType;
|
||||
@@ -31,15 +33,15 @@ public class ScoutingDataWriter {
|
||||
switch (data[i].getValueType()){
|
||||
case NUM:
|
||||
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;
|
||||
case STRING:
|
||||
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;
|
||||
case NUMARR:
|
||||
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();
|
||||
@@ -82,17 +84,17 @@ public class ScoutingDataWriter {
|
||||
case 1: // Int
|
||||
rawDataTypes[i] = IntType.newNull(values[version][i].UUID);
|
||||
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;
|
||||
case 2: // String
|
||||
rawDataTypes[i] = StringType.newNull(values[version][i].UUID);
|
||||
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;
|
||||
case 3: // Int array
|
||||
rawDataTypes[i] = IntArrType.newNull(values[version][i].UUID);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.ridgebotics.ridgescout.types;
|
||||
|
||||
public class ColabArray {
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.ridgebotics.ridgescout.types;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.ridgebotics.ridgescout.scoutingData.transfer.CreateTransferType;
|
||||
import com.ridgebotics.ridgescout.scoutingData.transfer.DirectTransferType;
|
||||
import com.ridgebotics.ridgescout.scoutingData.transfer.TransferType;
|
||||
@@ -44,7 +46,7 @@ public class ScoutingArray {
|
||||
}
|
||||
this.array = new_values;
|
||||
version++;
|
||||
System.out.println("Updated to " + version);
|
||||
Log.i(getClass().toString(),"Updated to " + version);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,14 +9,13 @@ import com.ridgebotics.ridgescout.utility.ByteBuilder;
|
||||
import com.ridgebotics.ridgescout.utility.SettingsManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
// Class to contain data for an entire event.
|
||||
// Easily encoded and decoded to binary format.
|
||||
public class frcEvent {
|
||||
|
||||
public static final int typecode = 254;
|
||||
public String eventCode;
|
||||
public String name;
|
||||
public ArrayList<frcMatch> matches;
|
||||
@@ -134,29 +133,29 @@ public class frcEvent {
|
||||
return null;
|
||||
}
|
||||
|
||||
// public
|
||||
|
||||
// Returns the soonest match that there will be all the possible upcoming data on other teams
|
||||
public void getReportMatches(int ourTeamNum){
|
||||
frcMatch[] teamMatches = event.getTeamMatches(ourTeamNum);
|
||||
public int getMostInformedBy(int ourTeamNum, int curMatch){
|
||||
frcMatch teamMatch = getNextTeamMatch(ourTeamNum, curMatch);
|
||||
|
||||
for(int i = 0; i < teamMatches.length; i++){
|
||||
int maxMatch = -1;
|
||||
for(int a = 0; a < 6; a++){
|
||||
int teamNum;
|
||||
if(a < 3)
|
||||
teamNum = teamMatches[i].redAlliance[a];
|
||||
else
|
||||
teamNum = teamMatches[i].blueAlliance[a-3];
|
||||
int maxMatch = Integer.MIN_VALUE;
|
||||
|
||||
if(teamNum == ourTeamNum)
|
||||
continue;
|
||||
for(int a = 0; a < 6; a++){
|
||||
int teamNum;
|
||||
if(a < 3)
|
||||
teamNum = teamMatch.redAlliance[a];
|
||||
else
|
||||
teamNum = teamMatch.blueAlliance[a-3];
|
||||
|
||||
int matchNum = event.getMostRecentTeamMatch(teamNum, teamMatches[i].matchIndex);
|
||||
if(maxMatch < matchNum)
|
||||
maxMatch = matchNum;
|
||||
}
|
||||
if(teamNum == ourTeamNum)
|
||||
continue;
|
||||
|
||||
int matchNum = event.getMostRecentTeamMatch(teamNum, teamMatch.matchIndex);
|
||||
if(maxMatch < matchNum)
|
||||
maxMatch = matchNum;
|
||||
}
|
||||
|
||||
return maxMatch;
|
||||
}
|
||||
|
||||
public frcTeam getTeamByNum(int teamNum){
|
||||
@@ -168,6 +167,26 @@ public class frcEvent {
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<frcTeam> getTeamsSorted() {
|
||||
int[] teamNums = new int[teams.size()];
|
||||
|
||||
for(int i = 0 ; i < teams.size(); i++){
|
||||
teamNums[i] = teams.get(i).teamNumber;
|
||||
}
|
||||
|
||||
Arrays.sort(teamNums);
|
||||
|
||||
List<frcTeam> list = new ArrayList<>();
|
||||
|
||||
for(int i = 0; i < teams.size(); i++){
|
||||
frcTeam team = getTeamByNum(teamNums[i]);
|
||||
assert team != null;
|
||||
list.add(team);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
public boolean getIsBlueAlliance(int teamNum, int matchNum){
|
||||
return getIsBlueAlliance(teamNum, matches.get(matchNum));
|
||||
|
||||
@@ -19,9 +19,12 @@ import android.view.ViewGroup;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.ridgebotics.ridgescout.R;
|
||||
import com.ridgebotics.ridgescout.types.frcTeam;
|
||||
import com.ridgebotics.ridgescout.ui.views.FieldBorderedRow;
|
||||
import com.ridgebotics.ridgescout.ui.views.RecyclerList;
|
||||
import com.ridgebotics.ridgescout.ui.views.TeamListOption;
|
||||
import com.ridgebotics.ridgescout.utility.DataManager;
|
||||
import com.ridgebotics.ridgescout.utility.SettingsManager;
|
||||
@@ -44,7 +47,7 @@ public class DataFragment extends Fragment {
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
|
||||
binding = FragmentDataBinding.inflate(inflater, container, false);
|
||||
binding.table.setStretchAllColumns(true);
|
||||
// binding.table.setStretchAllColumns(true);
|
||||
View root = binding.getRoot();
|
||||
|
||||
if(evcode == null || evcode.equals("unset") || event == null){
|
||||
@@ -86,35 +89,21 @@ public class DataFragment extends Fragment {
|
||||
|
||||
public void load_teams(){
|
||||
|
||||
int[] teamNums = new int[event.teams.size()];
|
||||
RecyclerList<frcTeam> list = new RecyclerList<>(getContext());
|
||||
binding.table.addView(list);
|
||||
// list.setView
|
||||
|
||||
for(int i = 0 ; i < event.teams.size(); i++){
|
||||
teamNums[i] = event.teams.get(i).teamNumber;
|
||||
}
|
||||
|
||||
Arrays.sort(teamNums);
|
||||
|
||||
for(int i = 0; i < event.teams.size(); i++){
|
||||
frcTeam team = null;
|
||||
for(int a = 0 ; a < event.teams.size(); a++){
|
||||
if(event.teams.get(a).teamNumber == teamNums[i]){
|
||||
team = event.teams.get(a);
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert team != null;
|
||||
|
||||
TeamListOption teamRow = new TeamListOption(getContext());
|
||||
binding.table.addView(teamRow);
|
||||
teamRow.fromTeam(team);
|
||||
|
||||
frcTeam finalTeam = team;
|
||||
teamRow.setOnClickListener(v -> {
|
||||
TeamsFragment.setTeam(finalTeam);
|
||||
list
|
||||
.setup(R.layout.view_team_option, TeamListOption::new)
|
||||
.withLinearLayout()
|
||||
.withDivider()
|
||||
.withItemClickListener((team, position) -> {
|
||||
TeamsFragment.setTeam(team);
|
||||
((DataParentFragment) getParentFragment()).moveToFragment(new TeamsFragment());
|
||||
// findNavController(this).navigate(R.id.action_navigation_data_parent_to_navigation_data_teams);
|
||||
});
|
||||
}
|
||||
|
||||
list.setItems(event.getTeamsSorted());
|
||||
|
||||
}
|
||||
public void load_fields(){
|
||||
DataManager.reload_match_fields();
|
||||
|
||||
@@ -10,6 +10,7 @@ import static com.ridgebotics.ridgescout.utility.DataManager.rescout_list;
|
||||
|
||||
import android.app.ProgressDialog;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -63,7 +64,7 @@ public class FieldDataFragment extends Fragment {
|
||||
|
||||
for (int i = 0; i < filenames.size(); i++) {
|
||||
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);
|
||||
if (psda.data.array[fieldIndex] != null && psda.data.array[fieldIndex].get() != null && !psda.data.array[fieldIndex].isNull())
|
||||
teamData.add(psda.data.array[fieldIndex]);
|
||||
@@ -75,7 +76,7 @@ public class FieldDataFragment extends Fragment {
|
||||
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.os.Bundle;
|
||||
import android.text.InputType;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -276,7 +277,7 @@ public class EventFragment extends Fragment {
|
||||
builder.setPositiveButton("OK", (dialogInterface, i) -> {
|
||||
|
||||
int index = dropdown.getIndex();
|
||||
System.out.println(index);
|
||||
Log.i(getClass().toString(), String.valueOf(index));
|
||||
if(!(index >= 0 && index < teamNums.size())) return;
|
||||
|
||||
event.teams.remove(index);
|
||||
|
||||
+20
-18
@@ -9,6 +9,7 @@ import static com.ridgebotics.ridgescout.utility.DataManager.event;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -48,8 +49,9 @@ public class MatchScoutingFragment extends Fragment {
|
||||
alliance_position = SettingsManager.getAllyPos();
|
||||
username = SettingsManager.getUsername();
|
||||
|
||||
binding.username.setText(username);
|
||||
binding.alliancePosText.setText(alliance_position);
|
||||
binding.bindicator.setUsername(username);
|
||||
binding.bindicator.setAlliancePos(alliance_position);
|
||||
binding.bindicator.bringToFront();
|
||||
|
||||
binding.matchTeamCard.setVisibility(View.GONE);
|
||||
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();
|
||||
SettingsManager.setMatchNum(cur_match_num+1);
|
||||
cur_match_num += 1;
|
||||
@@ -74,21 +76,21 @@ public class MatchScoutingFragment extends Fragment {
|
||||
});
|
||||
|
||||
if(SettingsManager.getEnableQuickAlliancePosChange())
|
||||
binding.fileIndicator.setOnClickListener(v -> {
|
||||
binding.bindicator.setOnClickListener(v -> {
|
||||
// if(e.getAction() != MotionEvent.ACTION_MOVE) return true;
|
||||
// System.out.println(e.getAxisValue(0));
|
||||
if(edited) save();
|
||||
|
||||
alliance_position = incrementMatchPos(alliance_position);
|
||||
SettingsManager.setAllyPos(alliance_position);
|
||||
binding.alliancePosText.setText(alliance_position);
|
||||
binding.bindicator.setAlliancePos(alliance_position);
|
||||
|
||||
update_match_num();
|
||||
update_scouting_data();
|
||||
// return true;
|
||||
});
|
||||
|
||||
binding.backButton.setOnClickListener(v -> {
|
||||
binding.bindicator.match_indicator_back_button.setOnClickListener(v -> {
|
||||
if(edited) save();
|
||||
SettingsManager.setMatchNum(cur_match_num-1);
|
||||
cur_match_num -= 1;
|
||||
@@ -150,7 +152,7 @@ public class MatchScoutingFragment extends Fragment {
|
||||
|
||||
|
||||
public void save(){
|
||||
System.out.println("Saved!");
|
||||
Log.i(this.getClass().toString(), "Saved!");
|
||||
edited = false;
|
||||
enableRescoutButton();
|
||||
AlertManager.toast("Saved " + filename);
|
||||
@@ -158,7 +160,7 @@ public class MatchScoutingFragment extends Fragment {
|
||||
}
|
||||
|
||||
public void set_indicator_color(int color){
|
||||
binding.fileIndicator.setBackgroundColor(color);
|
||||
binding.bindicator.setColor(color);
|
||||
}
|
||||
|
||||
public void update_asm(){
|
||||
@@ -238,18 +240,18 @@ public class MatchScoutingFragment extends Fragment {
|
||||
|
||||
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){
|
||||
binding.backButton.setVisibility(View.GONE);
|
||||
binding.bindicator.match_indicator_back_button.setVisibility(View.GONE);
|
||||
}else{
|
||||
binding.backButton.setVisibility(View.VISIBLE);
|
||||
binding.bindicator.match_indicator_back_button.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
if(cur_match_num >= event.matches.size()-1){
|
||||
binding.nextButton.setVisibility(View.GONE);
|
||||
binding.bindicator.match_indicator_next_button.setVisibility(View.GONE);
|
||||
}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;
|
||||
}
|
||||
|
||||
binding.barTeamNum.setText(String.valueOf(team_num));
|
||||
binding.bindicator.setTeamNum(String.valueOf(team_num));
|
||||
|
||||
frcTeam team = null;
|
||||
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))
|
||||
System.out.println("Saved!");
|
||||
Log.i(getClass().toString(), "Saved!");
|
||||
else
|
||||
System.out.println("Error saving");
|
||||
Log.i(getClass().toString(), "Error saving");
|
||||
}
|
||||
|
||||
private void enableRescoutButton(){
|
||||
set_indicator_color(rescout ? rescout_color : saved_color);
|
||||
binding.fileIndicator.setOnLongClickListener(v -> {
|
||||
binding.bindicator.setOnLongClickListener(v -> {
|
||||
rescout = !rescout;
|
||||
if(rescout){
|
||||
set_indicator_color(rescout_color);
|
||||
@@ -401,6 +403,6 @@ public class MatchScoutingFragment extends Fragment {
|
||||
}
|
||||
|
||||
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 android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -19,6 +20,7 @@ import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.google.android.material.divider.MaterialDivider;
|
||||
import com.ridgebotics.ridgescout.ui.views.PitScoutingIndicator;
|
||||
import com.ridgebotics.ridgescout.ui.views.ToggleTitleView;
|
||||
import com.ridgebotics.ridgescout.utility.AlertManager;
|
||||
import com.ridgebotics.ridgescout.utility.SettingsManager;
|
||||
@@ -75,7 +77,6 @@ public class PitScoutingFragment extends Fragment {
|
||||
|
||||
String fileUsernames = "";
|
||||
ToggleTitleView[] titles;
|
||||
|
||||
AutoSaveManager asm = new AutoSaveManager(this::save, AUTO_SAVE_DELAY);
|
||||
|
||||
ArrayList<RawDataType> rawDataTypes;
|
||||
@@ -91,14 +92,14 @@ public class PitScoutingFragment extends Fragment {
|
||||
}
|
||||
|
||||
if(ScoutingDataWriter.save(pit_values.length-1, ScoutingDataWriter.checkAddName(fileUsernames, username), filename, types)) {
|
||||
System.out.println("Saved!");
|
||||
AlertManager.toast("Saved " + filename);
|
||||
Log.i(getClass().toString(), "Saved!");
|
||||
Log.i(getClass().toString(), "Saved " + filename);
|
||||
}else
|
||||
System.out.println("Error saving");
|
||||
Log.i(getClass().toString(), "Error saving");
|
||||
}
|
||||
|
||||
public void set_indicator_color(int color){
|
||||
binding.pitFileIndicator.setBackgroundColor(color);
|
||||
binding.pitIndicator.setColor(color);
|
||||
}
|
||||
|
||||
public void update_asm(){
|
||||
@@ -113,10 +114,9 @@ public class PitScoutingFragment extends Fragment {
|
||||
public void loadTeam(){
|
||||
// clear_fields();
|
||||
|
||||
binding.pitFileIndicator.setVisibility(View.VISIBLE);
|
||||
binding.pitsTeamCard.setVisibility(View.VISIBLE);
|
||||
binding.pitBarTeamNum.setText(String.valueOf(team.teamNumber));
|
||||
binding.pitUsername.setText(SettingsManager.getUsername());
|
||||
binding.pitIndicator.setTeamNum(team.teamNumber);
|
||||
binding.pitIndicator.setUsername(SettingsManager.getUsername());
|
||||
binding.pitsTeamCard.fromTeam(team);
|
||||
|
||||
filename = evcode + "-" + team.teamNumber + ".pitscoutdata";
|
||||
@@ -145,7 +145,7 @@ public class PitScoutingFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
binding.pitFileIndicator.bringToFront();
|
||||
binding.pitIndicator.bringToFront();
|
||||
|
||||
asm.start();
|
||||
|
||||
@@ -153,7 +153,7 @@ public class PitScoutingFragment extends Fragment {
|
||||
|
||||
private void enableRescoutButton(){
|
||||
set_indicator_color(rescout ? rescout_color : saved_color);
|
||||
binding.pitFileIndicator.setOnLongClickListener(v -> {
|
||||
binding.pitIndicator.setOnLongClickListener(v -> {
|
||||
rescout = !rescout;
|
||||
if(rescout){
|
||||
set_indicator_color(rescout_color);
|
||||
@@ -170,7 +170,7 @@ public class PitScoutingFragment extends Fragment {
|
||||
}
|
||||
|
||||
private void disableRescoutButton(){
|
||||
binding.pitFileIndicator.setOnLongClickListener(null);
|
||||
binding.pitIndicator.setOnLongClickListener(null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -51,89 +51,89 @@ public class PitSelectorFragment extends Fragment {
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
load_teams();
|
||||
// load_teams();
|
||||
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
public void load_teams(){
|
||||
// binding.pitFileIndicator.setVisibility(View.GONE);
|
||||
// binding.pitTeamName.setVisibility(View.GONE);
|
||||
// binding.pitTeamDescription.setVisibility(View.GONE);
|
||||
// public void load_teams(){
|
||||
//// binding.pitFileIndicator.setVisibility(View.GONE);
|
||||
//// binding.pitTeamName.setVisibility(View.GONE);
|
||||
//// binding.pitTeamDescription.setVisibility(View.GONE);
|
||||
////
|
||||
//// clear_fields();
|
||||
//
|
||||
// clear_fields();
|
||||
|
||||
|
||||
int[] teamNums = new int[event.teams.size()];
|
||||
|
||||
for(int i = 0 ; i < event.teams.size(); i++){
|
||||
teamNums[i] = event.teams.get(i).teamNumber;
|
||||
}
|
||||
|
||||
Arrays.sort(teamNums);
|
||||
|
||||
TableLayout table = new TableLayout(getContext());
|
||||
table.setStretchAllColumns(true);
|
||||
binding.teams.addView(table);
|
||||
|
||||
|
||||
for(int i = 0; i < event.teams.size(); i++){
|
||||
frcTeam team = null;
|
||||
for(int a = 0 ; a < event.teams.size(); a++){
|
||||
if(event.teams.get(a).teamNumber == teamNums[i]){
|
||||
team = event.teams.get(a);
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert team != null;
|
||||
|
||||
// TableRow tr = new TableRow(getContext());
|
||||
// TableLayout.LayoutParams rowParams = new TableLayout.LayoutParams(
|
||||
// FrameLayout.LayoutParams.WRAP_CONTENT,
|
||||
// FrameLayout.LayoutParams.WRAP_CONTENT
|
||||
// );
|
||||
// rowParams.setMargins(20,20,20,20);
|
||||
// tr.setLayoutParams(rowParams);
|
||||
// tr.setPadding(20,20,20,20);
|
||||
// table.addView(tr);
|
||||
|
||||
TeamListOption teamRow = new TeamListOption(getContext());
|
||||
table.addView(teamRow);
|
||||
teamRow.fromTeam(team);
|
||||
|
||||
|
||||
String filename = evcode + "-" + team.teamNumber + ".pitscoutdata";
|
||||
|
||||
if (FileEditor.fileExist(filename)) {
|
||||
final boolean[] rescout = {DataManager.rescout_list.contains(filename)};
|
||||
|
||||
teamRow.setColor(DataManager.rescout_list.contains(filename) ? color_rescout : color_found);
|
||||
|
||||
teamRow.setOnLongClickListener(v -> {
|
||||
rescout[0] = !rescout[0];
|
||||
if(rescout[0]){
|
||||
DataManager.rescout_list.add(filename);
|
||||
teamRow.setColor(color_rescout);
|
||||
DataManager.save_rescout_list();
|
||||
}else{
|
||||
DataManager.rescout_list.remove(filename);
|
||||
teamRow.setColor(color_found);
|
||||
DataManager.save_rescout_list();
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
});
|
||||
} else {
|
||||
teamRow.setColor(color_not_found);
|
||||
teamRow.setOnLongClickListener(v -> true);
|
||||
}
|
||||
|
||||
|
||||
frcTeam finalTeam = team;
|
||||
teamRow.setOnClickListener(v -> onSelect.onSelect(this, finalTeam));
|
||||
}
|
||||
}
|
||||
//
|
||||
// int[] teamNums = new int[event.teams.size()];
|
||||
//
|
||||
// for(int i = 0 ; i < event.teams.size(); i++){
|
||||
// teamNums[i] = event.teams.get(i).teamNumber;
|
||||
// }
|
||||
//
|
||||
// Arrays.sort(teamNums);
|
||||
//
|
||||
// TableLayout table = new TableLayout(getContext());
|
||||
// table.setStretchAllColumns(true);
|
||||
// binding.teams.addView(table);
|
||||
//
|
||||
//
|
||||
// for(int i = 0; i < event.teams.size(); i++){
|
||||
// frcTeam team = null;
|
||||
// for(int a = 0 ; a < event.teams.size(); a++){
|
||||
// if(event.teams.get(a).teamNumber == teamNums[i]){
|
||||
// team = event.teams.get(a);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// assert team != null;
|
||||
//
|
||||
//// TableRow tr = new TableRow(getContext());
|
||||
//// TableLayout.LayoutParams rowParams = new TableLayout.LayoutParams(
|
||||
//// FrameLayout.LayoutParams.WRAP_CONTENT,
|
||||
//// FrameLayout.LayoutParams.WRAP_CONTENT
|
||||
//// );
|
||||
//// rowParams.setMargins(20,20,20,20);
|
||||
//// tr.setLayoutParams(rowParams);
|
||||
//// tr.setPadding(20,20,20,20);
|
||||
//// table.addView(tr);
|
||||
//
|
||||
// TeamListOption teamRow = new TeamListOption(getContext());
|
||||
// table.addView(teamRow);
|
||||
// teamRow.fromTeam(team);
|
||||
//
|
||||
//
|
||||
// String filename = evcode + "-" + team.teamNumber + ".pitscoutdata";
|
||||
//
|
||||
// if (FileEditor.fileExist(filename)) {
|
||||
// final boolean[] rescout = {DataManager.rescout_list.contains(filename)};
|
||||
//
|
||||
// teamRow.setColor(DataManager.rescout_list.contains(filename) ? color_rescout : color_found);
|
||||
//
|
||||
// teamRow.setOnLongClickListener(v -> {
|
||||
// rescout[0] = !rescout[0];
|
||||
// if(rescout[0]){
|
||||
// DataManager.rescout_list.add(filename);
|
||||
// teamRow.setColor(color_rescout);
|
||||
// DataManager.save_rescout_list();
|
||||
// }else{
|
||||
// DataManager.rescout_list.remove(filename);
|
||||
// teamRow.setColor(color_found);
|
||||
// DataManager.save_rescout_list();
|
||||
// }
|
||||
//
|
||||
//
|
||||
// return true;
|
||||
// });
|
||||
// } else {
|
||||
// teamRow.setColor(color_not_found);
|
||||
// teamRow.setOnLongClickListener(v -> true);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// frcTeam finalTeam = team;
|
||||
// teamRow.setOnClickListener(v -> onSelect.onSelect(this, finalTeam));
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -22,6 +23,7 @@ import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.ridgebotics.ridgescout.R;
|
||||
import com.ridgebotics.ridgescout.types.frcEvent;
|
||||
import com.ridgebotics.ridgescout.utility.AlertManager;
|
||||
import com.ridgebotics.ridgescout.utility.FileEditor;
|
||||
import com.ridgebotics.ridgescout.utility.SettingsManager;
|
||||
import com.ridgebotics.ridgescout.databinding.FragmentScoutingBinding;
|
||||
@@ -88,7 +90,7 @@ public class ScoutingFragment extends Fragment {
|
||||
|
||||
binding.textMatchAlliance.setVisibility(View.GONE);
|
||||
binding.textName.setVisibility(View.GONE);
|
||||
binding.textNextMatch.setVisibility(View.GONE);
|
||||
binding.infoBox.setVisibility(View.GONE);
|
||||
binding.textRescoutIndicator.setVisibility(View.GONE);
|
||||
|
||||
binding.matchScoutingButton.setEnabled(false);
|
||||
@@ -122,15 +124,7 @@ public class ScoutingFragment extends Fragment {
|
||||
findNavController(this).navigate(R.id.action_navigation_scouting_to_navigation_scouting_event);
|
||||
});
|
||||
|
||||
|
||||
binding.textName.setText("Welcome, " + SettingsManager.getUsername() + "!");
|
||||
|
||||
int matchNum = SettingsManager.getMatchNum();
|
||||
int nextMatch = event.getNextTeamMatch(SettingsManager.getTeamNum(), matchNum).matchIndex;
|
||||
|
||||
binding.textNextMatch.setText("Our next match: Match " + nextMatch);
|
||||
binding.textMatchAlliance.setText("Match: " + (matchNum+1) + ", " + SettingsManager.getAllyPos());
|
||||
binding.textRescoutIndicator.setText("Things to rescout: " + DataManager.rescout_list.size());
|
||||
updateDashboard();
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
@@ -159,4 +153,32 @@ public class ScoutingFragment extends Fragment {
|
||||
});
|
||||
}
|
||||
|
||||
private void updateDashboard() {
|
||||
binding.textName.setText("Welcome, " + SettingsManager.getUsername() + "!");
|
||||
|
||||
int curMatchNum = SettingsManager.getMatchNum();
|
||||
int nextMatch;
|
||||
int teamNum = SettingsManager.getTeamNum();
|
||||
try {
|
||||
nextMatch = event.getNextTeamMatch(teamNum, curMatchNum).matchIndex;
|
||||
} catch (Exception e){
|
||||
AlertManager.error("Sorry, in event ("+evcode+"), your team number ("+teamNum+") wasn't found!", e);
|
||||
return;
|
||||
}
|
||||
|
||||
binding.textMatchAlliance.setText("Match: " + (curMatchNum+1) + ", " + SettingsManager.getAllyPos());
|
||||
binding.textRescoutIndicator.setText("Things to rescout: " + DataManager.rescout_list.size());
|
||||
|
||||
TextView nextMatchText = new TextView(getContext());
|
||||
nextMatchText.setText("Our next match: Match " + nextMatch);
|
||||
nextMatchText.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Body1);
|
||||
binding.infoBox.addView(nextMatchText);
|
||||
|
||||
int informedBy = event.getMostInformedBy(teamNum, curMatchNum);
|
||||
|
||||
TextView mostInformedText = new TextView(getContext());
|
||||
mostInformedText.setText("Most informed by: Match " + informedBy);
|
||||
mostInformedText.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Body1);
|
||||
binding.infoBox.addView(mostInformedText);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -20,6 +21,7 @@ import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.navigation.Navigation;
|
||||
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.ridgebotics.ridgescout.MainActivity;
|
||||
import com.ridgebotics.ridgescout.R;
|
||||
import com.ridgebotics.ridgescout.databinding.FragmentSettingsFieldsBinding;
|
||||
@@ -215,7 +217,7 @@ public class FieldsFragment extends Fragment {
|
||||
AlertDialog dialog = alert.create();
|
||||
dialog.show();
|
||||
|
||||
Button deleteButton = new Button(getContext());
|
||||
MaterialButton deleteButton = new MaterialButton(getContext());
|
||||
deleteButton.setText("DELETE");
|
||||
deleteButton.setOnClickListener(l -> {
|
||||
AlertDialog.Builder alert2 = new AlertDialog.Builder(getContext());
|
||||
@@ -270,7 +272,7 @@ public class FieldsFragment extends Fragment {
|
||||
|
||||
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()];
|
||||
for(int i = 0; i < values.size(); i++) {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.ridgebotics.ridgescout.ui.settings;
|
||||
|
||||
import static android.view.View.VISIBLE;
|
||||
import static android.widget.LinearLayout.HORIZONTAL;
|
||||
import static android.widget.LinearLayout.VERTICAL;
|
||||
import static androidx.navigation.fragment.FragmentKt.findNavController;
|
||||
import static com.ridgebotics.ridgescout.utility.SettingsManager.AllyPosKey;
|
||||
import static com.ridgebotics.ridgescout.utility.SettingsManager.CustomEventsKey;
|
||||
@@ -13,6 +15,7 @@ import static com.ridgebotics.ridgescout.utility.SettingsManager.UnameKey;
|
||||
import static com.ridgebotics.ridgescout.utility.SettingsManager.WifiModeKey;
|
||||
import static com.ridgebotics.ridgescout.utility.SettingsManager.YearNumKey;
|
||||
import static com.ridgebotics.ridgescout.utility.SettingsManager.defaults;
|
||||
import static com.ridgebotics.ridgescout.utility.SettingsManager.getEVCode;
|
||||
import static com.ridgebotics.ridgescout.utility.SettingsManager.getEditor;
|
||||
import static com.ridgebotics.ridgescout.utility.SettingsManager.prefs;
|
||||
|
||||
@@ -30,13 +33,17 @@ import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TableLayout;
|
||||
import android.widget.TableRow;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.google.android.material.checkbox.MaterialCheckBox;
|
||||
import com.google.android.material.divider.MaterialDivider;
|
||||
import com.google.android.material.textfield.TextInputEditText;
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
import com.ridgebotics.ridgescout.R;
|
||||
@@ -46,6 +53,9 @@ import com.ridgebotics.ridgescout.ui.views.CustomSpinnerView;
|
||||
import com.ridgebotics.ridgescout.ui.views.TallyCounterView;
|
||||
import com.ridgebotics.ridgescout.utility.DataManager;
|
||||
import com.ridgebotics.ridgescout.utility.FileEditor;
|
||||
import com.ridgebotics.ridgescout.utility.SettingsManager;
|
||||
|
||||
import org.w3c.dom.Text;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -64,20 +74,6 @@ public class SettingsFragment extends Fragment {
|
||||
|
||||
reloadSettings();
|
||||
|
||||
binding.fieldsButton.setOnClickListener(v -> {
|
||||
binding.fieldsButton.setEnabled(false);
|
||||
binding.fieldsButtons.setVisibility(VISIBLE);
|
||||
});
|
||||
|
||||
binding.fieldsMatchesButton.setOnClickListener(v -> {
|
||||
FieldsFragment.set_filename(Fields.matchFieldsFilename);
|
||||
findNavController(this).navigate(R.id.action_navigation_settings_to_navigation_data_fields);
|
||||
});
|
||||
|
||||
binding.fieldsPitsButton.setOnClickListener(v -> {
|
||||
FieldsFragment.set_filename(Fields.pitsFieldsFilename);
|
||||
findNavController(this).navigate(R.id.action_navigation_settings_to_navigation_data_fields);
|
||||
});
|
||||
|
||||
|
||||
return root;
|
||||
@@ -90,26 +86,67 @@ public class SettingsFragment extends Fragment {
|
||||
SettingsManager manager = new SettingsManager(getContext());
|
||||
|
||||
|
||||
|
||||
ButtonSettingsItem corruptButton = new ButtonSettingsItem();
|
||||
corruptButton.addButton("Remove corrupted files", view -> {});
|
||||
|
||||
manager.addItem(corruptButton);
|
||||
manager.addItem(new HeaderSettingsItem("Advanced"));
|
||||
|
||||
|
||||
|
||||
|
||||
ButtonSettingsItem fieldsButtons = new ButtonSettingsItem();
|
||||
fieldsButtons.addButton("Edit pit fields", v -> {
|
||||
FieldsFragment.set_filename(Fields.pitsFieldsFilename);
|
||||
findNavController(this).navigate(R.id.action_navigation_settings_to_navigation_data_fields);
|
||||
});
|
||||
fieldsButtons.addButton("Edit match fields", v -> {
|
||||
FieldsFragment.set_filename(Fields.matchFieldsFilename);
|
||||
findNavController(this).navigate(R.id.action_navigation_settings_to_navigation_data_fields);
|
||||
});
|
||||
manager.addItem(fieldsButtons);
|
||||
|
||||
ButtonSettingsItem noticeButton = new ButtonSettingsItem();
|
||||
noticeButton.addButton("Edit scout notice", v->editNotice());
|
||||
noticeButton.setEnabled(!getEVCode().equals("unset"));
|
||||
manager.addItem(noticeButton);
|
||||
|
||||
|
||||
manager.addItem(new CheckboxSettingsItem(CustomEventsKey, "Custom Events"));
|
||||
|
||||
StringSettingsItem FTPServer = new StringSettingsItem(com.ridgebotics.ridgescout.utility.SettingsManager.FTPServer, "FTP Server (Sync)");
|
||||
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);
|
||||
CheckboxSettingsItem FTPEnabled = new CheckboxSettingsItem(com.ridgebotics.ridgescout.utility.SettingsManager.FTPEnabled, "FTP Enabled", FTPServer, FTPSendMetaFiles);
|
||||
|
||||
manager.addItem(new HeaderSettingsItem("Admin"));
|
||||
|
||||
|
||||
|
||||
|
||||
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);
|
||||
CheckboxSettingsItem FTPEnabled = new CheckboxSettingsItem(com.ridgebotics.ridgescout.utility.SettingsManager.FTPEnabled, "Sync Enabled", FTPServer, FTPKey, FTPSendMetaFiles);
|
||||
manager.addItem(FTPEnabled);
|
||||
|
||||
manager.addItem(new CheckboxSettingsItem(WifiModeKey, "Wifi Mode", FTPEnabled));
|
||||
manager.addItem(new CheckboxSettingsItem(EnableQuickAllianceChangeKey, "Enable quick alliance swap", null));
|
||||
|
||||
|
||||
manager.addItem(new NumberSettingsItem(YearNumKey, "Year", 0, 9999));
|
||||
|
||||
manager.addItem(new HeaderSettingsItem("Connection"));
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
manager.addItem(new CheckboxSettingsItem(EnableQuickAllianceChangeKey, "Enable quick alliance swap", null));
|
||||
manager.addItem(new DropdownSettingsItem(FieldImageKey, "Field Image", new String[]{
|
||||
"2025",
|
||||
"2025 (Flipped)"
|
||||
}));
|
||||
|
||||
manager.addItem(new NumberSettingsItem(YearNumKey, "Year", 0, 9999));
|
||||
|
||||
|
||||
manager.addItem(new DropdownSettingsItem(AllyPosKey, "Alliance Pos", alliance_pos_list));
|
||||
|
||||
int max = 0;
|
||||
@@ -131,16 +168,48 @@ public class SettingsFragment extends Fragment {
|
||||
|
||||
manager.addItem(new StringSettingsItem(UnameKey, "Username"));
|
||||
manager.addItem(new NumberSettingsItem(TeamNumKey, "Team Number", 0, 99999));
|
||||
manager.addItem(new HeaderSettingsItem("Scouting"));
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
binding.SettingsTable.removeAllViews();
|
||||
|
||||
manager.getView(binding.SettingsTable);
|
||||
|
||||
if(!DataManager.getevcode().equals("unset")){
|
||||
Button editNoticeButton = new Button(getContext());
|
||||
editNoticeButton.setText("Edit Scout Notice");
|
||||
binding.SettingsTable.addView(editNoticeButton);
|
||||
editNoticeButton.setOnClickListener(v->editNotice());
|
||||
}
|
||||
|
||||
// Add "Edit scout notice" button to the bottom of the page=
|
||||
|
||||
|
||||
// Add "Field edit" buttons to the bottom of the page
|
||||
// LinearLayout fieldButtons = new LinearLayout(getContext());
|
||||
// fieldButtons.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
// LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
// LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
// ));
|
||||
// fieldButtons.setOrientation(HORIZONTAL);
|
||||
//
|
||||
// Button editMatchFieldsButton = new Button(getContext());
|
||||
// editMatchFieldsButton.setText("Edit Match Fields");
|
||||
// editMatchFieldsButton.setOnClickListener(v -> {
|
||||
// FieldsFragment.set_filename(Fields.matchFieldsFilename);
|
||||
// findNavController(this).navigate(R.id.action_navigation_settings_to_navigation_data_fields);
|
||||
// });
|
||||
// fieldButtons.addView(editMatchFieldsButton);
|
||||
//
|
||||
//
|
||||
// Button editPitsFieldsButton = new Button(getContext());
|
||||
// editPitsFieldsButton.setText("Edit pits Fields");
|
||||
// editPitsFieldsButton.setOnClickListener(v -> {
|
||||
// FieldsFragment.set_filename(Fields.pitsFieldsFilename);
|
||||
// findNavController(this).navigate(R.id.action_navigation_settings_to_navigation_data_fields);
|
||||
// });
|
||||
// fieldButtons.addView(editPitsFieldsButton);
|
||||
|
||||
|
||||
// binding.SettingsTable.addView(fieldButtons);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -344,7 +413,7 @@ public class SettingsFragment extends Fragment {
|
||||
@Override
|
||||
public View createView(Context context) {
|
||||
LinearLayout ll = new LinearLayout(getContext());
|
||||
ll.setOrientation(LinearLayout.VERTICAL);
|
||||
ll.setOrientation(VERTICAL);
|
||||
|
||||
tally = new TallyCounterView(getContext());
|
||||
|
||||
@@ -472,6 +541,94 @@ public class SettingsFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
public class HeaderSettingsItem extends SettingsItem<Void> {
|
||||
String title;
|
||||
|
||||
public HeaderSettingsItem(String title) {
|
||||
super("", title, null);
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled){
|
||||
}
|
||||
|
||||
@Override
|
||||
public View createView(Context context) {
|
||||
LinearLayout ll = new LinearLayout(context);
|
||||
ll.setOrientation(VERTICAL);
|
||||
ll.setPadding(0, 20,0,0);
|
||||
|
||||
TextView tv = new TextView(context);
|
||||
tv.setText(title);
|
||||
tv.setTextAppearance(com.google.android.material.R.style.TextAppearance_MaterialComponents_Headline4);
|
||||
ll.addView(tv);
|
||||
|
||||
ll.addView(new MaterialDivider(context));
|
||||
|
||||
return ll;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void getValue() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public class ButtonSettingsItem extends SettingsItem<Void> {
|
||||
List<MaterialButton> buttons = new ArrayList<>();
|
||||
List<String> titles = new ArrayList<>();
|
||||
List<View.OnClickListener> callbacks = new ArrayList<>();
|
||||
|
||||
boolean enabled = true;
|
||||
|
||||
public ButtonSettingsItem() {
|
||||
super("", "", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled){
|
||||
this.enabled = enabled;
|
||||
for(int i = 0; i < buttons.size(); i++){
|
||||
buttons.get(i).setEnabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
public void addButton(String text, View.OnClickListener onClickListener) {
|
||||
titles.add(text);
|
||||
callbacks.add(onClickListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View createView(Context context) {
|
||||
LinearLayout ll = new LinearLayout(context);
|
||||
ll.setOrientation(HORIZONTAL);
|
||||
|
||||
for(int i = 0; i < titles.size(); i++){
|
||||
MaterialButton button = new MaterialButton(context);
|
||||
button.setText(titles.get(i));
|
||||
button.setOnClickListener(callbacks.get(i));
|
||||
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT, 1.0f);
|
||||
layoutParams.setMargins(3, 0, 3, 0);
|
||||
// layoutParams.weight
|
||||
button.setLayoutParams(layoutParams);
|
||||
button.setEnabled(enabled);
|
||||
// button.weight
|
||||
buttons.add(button);
|
||||
|
||||
ll.addView(button);
|
||||
}
|
||||
|
||||
return ll;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void getValue() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public class SettingsManager {
|
||||
private Context context;
|
||||
private HashMap<String, Object> settings;
|
||||
@@ -494,7 +651,7 @@ public class SettingsFragment extends Fragment {
|
||||
items.add(item);
|
||||
|
||||
LinearLayout itemContainer = new LinearLayout(context);
|
||||
itemContainer.setOrientation(LinearLayout.VERTICAL);
|
||||
itemContainer.setOrientation(VERTICAL);
|
||||
itemContainer.setPadding(32, 0, 32, 8);
|
||||
|
||||
View view = item.createView(context);
|
||||
|
||||
@@ -3,6 +3,8 @@ package com.ridgebotics.ridgescout.ui.transfer;
|
||||
import static com.ridgebotics.ridgescout.utility.DataManager.evcode;
|
||||
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;
|
||||
@@ -26,6 +28,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
// This is now deprecated. use HTTPSync
|
||||
// Class to synchronise data over FTP.
|
||||
public class FTPSync extends Thread {
|
||||
public static final String remoteBasePath = "/RidgeScout/";
|
||||
@@ -156,13 +159,13 @@ public class FTPSync extends Thread {
|
||||
|
||||
if (remoteTimestamp == null || after(localTimeStamp, remoteTimestamp)) {
|
||||
uploadFile(localFile);
|
||||
System.out.println("Uploaded" + localFile.getName());
|
||||
Log.i(getClass().toString(), "Uploaded" + localFile.getName());
|
||||
|
||||
setLocalFileTimestamp(localFile, curSyncTime);
|
||||
remoteTimestamps.put(localFile.getName(), curSyncTime);
|
||||
upCount++;
|
||||
}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))) {
|
||||
downloadFile(remoteFile, localFile);
|
||||
|
||||
System.out.println("Downloaded " + localFile.getName());
|
||||
Log.i(getClass().toString(), "Downloaded " + localFile.getName());
|
||||
|
||||
if(!localFile.exists()) System.out.println("Not exist");
|
||||
else if(after(remoteTimestamp, localTimeStamp)) System.out.println("Before: " + (localTimeStamp.getTime()-remoteTimestamp.getTime()));
|
||||
if(!localFile.exists()) Log.i(getClass().toString(), "Not exist");
|
||||
else if(after(remoteTimestamp, localTimeStamp)) Log.i(getClass().toString(), "Before: " + (localTimeStamp.getTime()-remoteTimestamp.getTime()));
|
||||
|
||||
// Date d = getUtcTimestamp(remoteFile);
|
||||
setLocalFileTimestamp(localFile, remoteTimestamps.get(localFile.getName()));
|
||||
// remoteTimestamps.put(remoteFile, curSyncTime);
|
||||
downCount++;
|
||||
}else{
|
||||
System.out.println("Did not download");
|
||||
Log.i(getClass().toString(), "Did not download");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,368 @@
|
||||
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;
|
||||
|
||||
// Class to synchronise data over HTTP.
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.ridgebotics.ridgescout.R;
|
||||
import com.ridgebotics.ridgescout.databinding.FragmentTransferTbaBinding;
|
||||
import com.ridgebotics.ridgescout.types.frcEvent;
|
||||
@@ -49,8 +50,6 @@ public class TBAEventFragment extends Fragment {
|
||||
|
||||
private final int year = SettingsManager.getYearNum();
|
||||
|
||||
private ProgressDialog loadingDialog;
|
||||
|
||||
private static JSONObject eventData = null;
|
||||
public static void setEventData(JSONObject j){
|
||||
eventData = j;
|
||||
@@ -74,7 +73,7 @@ public class TBAEventFragment extends Fragment {
|
||||
|
||||
Table.setStretchAllColumns(true);
|
||||
|
||||
startLoading("Loading Teams and Matches...");
|
||||
AlertManager.startLoading("Loading Teams and Matches...");
|
||||
Table.removeAllViews();
|
||||
Table.setStretchAllColumns(true);
|
||||
Table.bringToFront();
|
||||
@@ -92,7 +91,7 @@ public class TBAEventFragment extends Fragment {
|
||||
final RequestTask rq1 = new RequestTask();
|
||||
rq1.onResult(matchesStr -> {
|
||||
matchTable(matchesStr, teamsStr, eventData);
|
||||
stopLoading();
|
||||
AlertManager.stopLoading();
|
||||
return null;
|
||||
});
|
||||
rq1.execute((TBAAddress + "event/" + matchKey + "/matches"), TBAHeader);
|
||||
@@ -153,7 +152,7 @@ public class TBAEventFragment extends Fragment {
|
||||
|
||||
|
||||
// Save button
|
||||
Button btn = new Button(getContext());
|
||||
MaterialButton btn = new MaterialButton(getContext());
|
||||
btn.setText("Save");
|
||||
btn.setTextSize(18);
|
||||
btn.setLayoutParams(new TableRow.LayoutParams(
|
||||
@@ -375,12 +374,12 @@ public class TBAEventFragment extends Fragment {
|
||||
|
||||
}catch (JSONException j){
|
||||
AlertManager.error("Failed Downloading", j);
|
||||
stopLoading();
|
||||
AlertManager.stopLoading();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean saveData(ArrayList<frcMatch> matchData, JSONArray teamData, JSONObject eventData){
|
||||
startLoading("Saving data...");
|
||||
AlertManager.startLoading("Saving data...");
|
||||
|
||||
Thread t = new Thread(() -> {
|
||||
try {
|
||||
@@ -431,31 +430,15 @@ public class TBAEventFragment extends Fragment {
|
||||
AlertManager.toast("Saved!");
|
||||
|
||||
getActivity().runOnUiThread(() -> findNavController(this).navigate(R.id.action_navigation_tba_event_to_navigation_transfer));
|
||||
stopLoading();
|
||||
AlertManager.stopLoading();
|
||||
|
||||
}catch(Exception j) {
|
||||
AlertManager.error(j);
|
||||
stopLoading();
|
||||
AlertManager.stopLoading();
|
||||
}
|
||||
});
|
||||
t.start();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void startLoading(String title){
|
||||
getActivity().runOnUiThread(() -> {
|
||||
if(loadingDialog != null && loadingDialog.isShowing())
|
||||
loadingDialog.dismiss();
|
||||
loadingDialog = ProgressDialog.show(getActivity(), title, "Please wait...");
|
||||
});
|
||||
}
|
||||
|
||||
private void stopLoading(){
|
||||
getActivity().runOnUiThread(() -> {
|
||||
if (loadingDialog != null)
|
||||
loadingDialog.cancel();
|
||||
loadingDialog = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,13 +65,13 @@ public class TransferFragment extends Fragment {
|
||||
|
||||
binding.SyncButton.setOnClickListener(v -> {
|
||||
binding.SyncButton.setEnabled(false);
|
||||
FTPSync.sync();
|
||||
HttpSync.sync();
|
||||
});
|
||||
|
||||
if(FTPSync.getIsRunning())
|
||||
if(HttpSync.getIsRunning())
|
||||
binding.SyncButton.setEnabled(false);
|
||||
|
||||
FTPSync.setOnResult((error, upcount, downcount) -> {
|
||||
HttpSync.setOnResult((error, upcount, downcount) -> {
|
||||
if (getActivity() != null)
|
||||
getActivity().runOnUiThread(() -> {
|
||||
binding.SyncButton.setEnabled(true);
|
||||
@@ -79,8 +79,8 @@ public class TransferFragment extends Fragment {
|
||||
});
|
||||
});
|
||||
|
||||
binding.syncIndicator.setText(FTPSync.text);
|
||||
FTPSync.setOnUpdateIndicator(text -> {if(getActivity() != null) getActivity().runOnUiThread(() -> binding.syncIndicator.setText(text));});
|
||||
binding.syncIndicator.setText(HttpSync.text);
|
||||
HttpSync.setOnUpdateIndicator(text -> {if(getActivity() != null) getActivity().runOnUiThread(() -> binding.syncIndicator.setText(text));});
|
||||
|
||||
if(evcode.equals("unset")){
|
||||
binding.noEventError.setVisibility(View.VISIBLE);
|
||||
|
||||
+2
-1
@@ -8,6 +8,7 @@ import android.bluetooth.BluetoothSocket;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
@@ -139,7 +140,7 @@ public class BluetoothReceiver {
|
||||
} catch (IOException e) {
|
||||
if (e.getMessage() != null && e.getMessage().contains("bt socket closed, read return: -1")) {
|
||||
receiveddata.onConnectionStop();
|
||||
System.out.println("Bluetooth socket closed, treating as end of stream");
|
||||
Log.i(getClass().toString(), "Bluetooth socket closed, treating as end of stream");
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,6 +1,7 @@
|
||||
package com.ridgebotics.ridgescout.ui.transfer.bluetooth;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -117,7 +118,7 @@ public class BluetoothReceiverFragment extends Fragment {
|
||||
|
||||
private void receiveData(byte[] data, int 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);
|
||||
}
|
||||
|
||||
@@ -138,7 +139,7 @@ public class BluetoothReceiverFragment extends Fragment {
|
||||
ScoutingFile f = ScoutingFile.decode((byte[]) result.get(i).get());
|
||||
|
||||
if (f != null) {
|
||||
System.out.println(f.filename);
|
||||
Log.i(getClass().toString(), f.filename);
|
||||
if (f.write())
|
||||
result_filenames += f.filename + "\n";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
package com.ridgebotics.ridgescout.ui.transfer.codes;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.os.AsyncTask;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.EncodeHintType;
|
||||
import com.google.zxing.MultiFormatWriter;
|
||||
import com.google.zxing.WriterException;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.ridgebotics.ridgescout.utility.AlertManager;
|
||||
import com.ridgebotics.ridgescout.utility.FileEditor;
|
||||
import com.ridgebotics.ridgescout.utility.TaskRunner;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class CodeGenTask implements Callable<List<Bitmap>> {
|
||||
// private Function<List<Bitmap>, String> resultFunction = null;
|
||||
//
|
||||
// @Override
|
||||
// protected List<Bitmap> doInBackground(String... strings) {
|
||||
//
|
||||
|
||||
//
|
||||
// return new ArrayList<>();
|
||||
// }
|
||||
//
|
||||
//
|
||||
// public void onResult(Function<List<Bitmap>, String> func) {
|
||||
// this.resultFunction = func;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// @Override
|
||||
// protected void onPostExecute(List<Bitmap> result) {
|
||||
// super.onPostExecute(result);
|
||||
// if(resultFunction != null){
|
||||
// resultFunction.apply(result);
|
||||
// }
|
||||
// }
|
||||
|
||||
private final String data;
|
||||
private final int randID;
|
||||
private final int qrSize;
|
||||
private final int qrCount;
|
||||
private final int imageSize;
|
||||
|
||||
public CodeGenTask(String data, int randID, int qrSize, int qrCount, int imageSize) {
|
||||
this.data = data;
|
||||
this.randID = randID;
|
||||
this.qrSize = qrSize;
|
||||
this.qrCount = qrCount;
|
||||
this.imageSize = imageSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Bitmap> call() {
|
||||
List<Bitmap> qrBitmaps = new ArrayList<>();
|
||||
|
||||
for(int i=0;i<=((data.length()+1)/qrSize);i++){
|
||||
final int start = i*qrSize;
|
||||
int end = (i+1)*qrSize;
|
||||
if(end >= data.length()){
|
||||
end = data.length();
|
||||
}
|
||||
try {
|
||||
Bitmap unscaledBitmap = generateQrCode(
|
||||
FileEditor.byteToChar(FileEditor.internalDataVersion, FileEditor.lengthHeaderBytes) +
|
||||
String.valueOf(FileEditor.byteToChar(randID, FileEditor.lengthHeaderBytes)) +
|
||||
FileEditor.byteToChar(i, FileEditor.lengthHeaderBytes) +
|
||||
FileEditor.byteToChar(qrCount - 1, FileEditor.lengthHeaderBytes) +
|
||||
data.substring(start, end)
|
||||
);
|
||||
if(unscaledBitmap == null) {
|
||||
AlertManager.error("Generated image was null!");
|
||||
continue;
|
||||
}
|
||||
qrBitmaps.add(Bitmap.createScaledBitmap(unscaledBitmap, imageSize, imageSize, false));
|
||||
// alert("title", ""+(qrCount-1));
|
||||
}catch (WriterException e){
|
||||
AlertManager.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
return qrBitmaps;
|
||||
}
|
||||
|
||||
private Bitmap generateQrCode(String contents) throws WriterException {
|
||||
|
||||
final int size = 512;
|
||||
|
||||
if (contents == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<EncodeHintType, Object> hints = new EnumMap<>(EncodeHintType.class);
|
||||
|
||||
// The Charset must be UTF-8, Or data will not be transferred properly. IDK why.
|
||||
hints.put(EncodeHintType.CHARACTER_SET, "ISO-8859-1");
|
||||
// hints.put(EncodeHintType.);
|
||||
hints.put(EncodeHintType.MARGIN, 0); /* default = 4 */
|
||||
MultiFormatWriter writer = new MultiFormatWriter();
|
||||
|
||||
BitMatrix result;
|
||||
try {
|
||||
result = writer.encode(contents, BarcodeFormat.DATA_MATRIX, size, size, hints);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Unsupported format
|
||||
AlertManager.error(e);
|
||||
return null;
|
||||
}
|
||||
|
||||
int width = result.getWidth();
|
||||
int height = result.getHeight();
|
||||
int[] pixels = new int[width * height];
|
||||
for (int y = 0; y < height; y++) {
|
||||
int offset = y * width;
|
||||
for (int x = 0; x < width; x++) {
|
||||
pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.WHITE;
|
||||
}
|
||||
}
|
||||
|
||||
Bitmap bitmap = Bitmap.createBitmap(width, height,
|
||||
Bitmap.Config.ARGB_8888);
|
||||
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
+73
-107
@@ -1,9 +1,10 @@
|
||||
package com.ridgebotics.ridgescout.ui.transfer.codes;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.os.CountDownTimer;
|
||||
import android.os.Handler;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -18,16 +19,11 @@ import androidx.fragment.app.Fragment;
|
||||
import com.ridgebotics.ridgescout.databinding.FragmentTransferCodeSenderBinding;
|
||||
import com.ridgebotics.ridgescout.utility.AlertManager;
|
||||
import com.ridgebotics.ridgescout.utility.FileEditor;
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.EncodeHintType;
|
||||
import com.google.zxing.MultiFormatWriter;
|
||||
import com.google.zxing.WriterException;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.ridgebotics.ridgescout.utility.TaskRunner;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
// Class to show the code transfer thing.
|
||||
@@ -38,23 +34,28 @@ public class CodeGeneratorView extends Fragment {
|
||||
private TextView qrIndexN;
|
||||
private TextView qrIndexD;
|
||||
|
||||
private final int maxQrCount = 256; //The max number that can be stored in a byte
|
||||
private static final int maxQrCount = 256; //The max number that can be stored in a byte
|
||||
|
||||
private final int maxQrSpeed = 5;
|
||||
private final int minQrSpeed = 300 + maxQrSpeed - 1;
|
||||
private static final int maxQrSize = 800;
|
||||
|
||||
|
||||
private static final int maxQrSpeed = 50;
|
||||
private static final int minQrSpeed = 1000;
|
||||
private static final int defaultQrDelay = 12;
|
||||
|
||||
|
||||
private int imageSize;
|
||||
|
||||
private int minQrSize = 0;
|
||||
private final int maxQrSize = 800;
|
||||
private int qrSize = 200;
|
||||
|
||||
private final int defaultQrDelay = 419;
|
||||
private int qrDelay = 0;
|
||||
private double qrDelay = 0;
|
||||
private int qrIndex = 0;
|
||||
|
||||
private CountDownTimer timer;
|
||||
private int qrCount = 0;
|
||||
|
||||
private ArrayList<Bitmap> qrBitmaps;
|
||||
private List<Bitmap> qrBitmaps = new ArrayList<>();
|
||||
|
||||
private FragmentTransferCodeSenderBinding binding;
|
||||
|
||||
@@ -77,6 +78,12 @@ public class CodeGeneratorView extends Fragment {
|
||||
qrIndexN = binding.qrIndexN;
|
||||
qrIndexD = binding.qrIndexD;
|
||||
|
||||
DisplayMetrics displaymetrics = new DisplayMetrics();
|
||||
getActivity().getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
|
||||
// int height = displaymetrics.heightPixels;
|
||||
imageSize = displaymetrics.widthPixels;
|
||||
// = 800;
|
||||
|
||||
String compressed = new String(FileEditor.blockCompress(data, FileEditor.lengthHeaderBytes), StandardCharsets.ISO_8859_1);
|
||||
|
||||
if(compressed.isEmpty()){
|
||||
@@ -85,75 +92,30 @@ public class CodeGeneratorView extends Fragment {
|
||||
}
|
||||
|
||||
minQrSize = Math.round((float)compressed.length() / maxQrCount)+1;
|
||||
|
||||
qrSizeSlider.setMax(maxQrSize-minQrSize);
|
||||
qrSpeedSlider.setMax((minQrSpeed-maxQrSpeed)*2);
|
||||
|
||||
qrSizeSlider.setProgress(minQrSize+qrSize);
|
||||
qrSpeedSlider.setProgress(defaultQrDelay+5);
|
||||
qrSize += minQrSize;
|
||||
|
||||
sendData(compressed);
|
||||
|
||||
qrSpeedSlider.setMax(maxQrSpeed*2);
|
||||
qrSpeedSlider.setProgress(maxQrSpeed + defaultQrDelay);
|
||||
|
||||
qrSizeSlider.setMax(maxQrSize-minQrSize);
|
||||
qrSizeSlider.setProgress(qrSize-minQrSize);
|
||||
|
||||
startLoop();
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
|
||||
private Bitmap generateQrCode(String contents) throws WriterException {
|
||||
|
||||
final int size = 512;
|
||||
|
||||
if (contents == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<EncodeHintType, Object> hints = new EnumMap<>(EncodeHintType.class);
|
||||
|
||||
// The Charset must be UTF-8, Or data will not be transferred properly. IDK why.
|
||||
hints.put(EncodeHintType.CHARACTER_SET, "ISO-8859-1");
|
||||
// hints.put(EncodeHintType.);
|
||||
hints.put(EncodeHintType.MARGIN, 0); /* default = 4 */
|
||||
MultiFormatWriter writer = new MultiFormatWriter();
|
||||
|
||||
BitMatrix result;
|
||||
try {
|
||||
result = writer.encode(contents, BarcodeFormat.DATA_MATRIX, size, size, hints);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Unsupported format
|
||||
AlertManager.error(e);
|
||||
return null;
|
||||
}
|
||||
|
||||
int width = result.getWidth();
|
||||
int height = result.getHeight();
|
||||
int[] pixels = new int[width * height];
|
||||
for (int y = 0; y < height; y++) {
|
||||
int offset = y * width;
|
||||
for (int x = 0; x < width; x++) {
|
||||
pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.WHITE;
|
||||
}
|
||||
}
|
||||
|
||||
Bitmap bitmap = Bitmap.createBitmap(width, height,
|
||||
Bitmap.Config.ARGB_8888);
|
||||
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
|
||||
private void sendData(String data){
|
||||
|
||||
|
||||
|
||||
qrCount = (data.length()/qrSize)+1;
|
||||
qrIndexD.setText(String.valueOf(qrCount));
|
||||
|
||||
// alert("size", ""+binding.qrSizeSlider.getProgress()+"\n"+binding.qrSizeSlider.getMax());
|
||||
|
||||
qrSpeedSlider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
qrDelay = -(minQrSpeed - progress - maxQrSpeed + 1);
|
||||
qrDelay = ((double) progress /maxQrSpeed) - 1;
|
||||
// startLoop();
|
||||
}
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {}
|
||||
@@ -173,43 +135,24 @@ public class CodeGeneratorView extends Fragment {
|
||||
qrCount = ((data.length()+1)/qrSize) +1;
|
||||
qrIndexD.setText(String.valueOf(qrCount));
|
||||
sendData(data);
|
||||
// startLoop();
|
||||
}
|
||||
});
|
||||
|
||||
// qrSizeSlider.setProgress(qr);
|
||||
AlertManager.startLoading("Generating codes...");
|
||||
|
||||
qrBitmaps = new ArrayList<>();
|
||||
new TaskRunner().executeAsync(new CodeGenTask(data, new Random().nextInt(255), qrSize, qrCount, imageSize), result -> {
|
||||
qrBitmaps = result;
|
||||
AlertManager.stopLoading();
|
||||
qrIndex = 0;
|
||||
});
|
||||
|
||||
int randID = new Random().nextInt(255);
|
||||
|
||||
for(int i=0;i<=((data.length()+1)/qrSize);i++){
|
||||
final int start = i*qrSize;
|
||||
int end = (i+1)*qrSize;
|
||||
if(end >= data.length()){
|
||||
end = data.length();
|
||||
}
|
||||
try {
|
||||
// alert("test", ""+Math.ceil((double)data.length()/(double)qrSize));
|
||||
qrBitmaps.add(generateQrCode(
|
||||
FileEditor.byteToChar(FileEditor.internalDataVersion, FileEditor.lengthHeaderBytes) +
|
||||
String.valueOf(FileEditor.byteToChar(randID, FileEditor.lengthHeaderBytes)) +
|
||||
FileEditor.byteToChar(i, FileEditor.lengthHeaderBytes) +
|
||||
FileEditor.byteToChar(qrCount - 1, FileEditor.lengthHeaderBytes) +
|
||||
data.substring(start, end)
|
||||
));
|
||||
// alert("title", ""+(qrCount-1));
|
||||
}catch (WriterException e){
|
||||
AlertManager.error(e);
|
||||
}
|
||||
}
|
||||
qrIndex = 0;
|
||||
if(timer != null){
|
||||
timer.cancel();
|
||||
}
|
||||
qrLoop();
|
||||
}
|
||||
|
||||
private void updateQr(){
|
||||
if(qrBitmaps.isEmpty())
|
||||
return;
|
||||
|
||||
qrImage.setImageBitmap(qrBitmaps.get(qrIndex));
|
||||
if(qrDelay > 0) {
|
||||
this.qrIndex += 1;
|
||||
@@ -226,13 +169,36 @@ public class CodeGeneratorView extends Fragment {
|
||||
qrIndexN.setText(String.valueOf(qrIndex+1));
|
||||
}
|
||||
|
||||
private void qrLoop(){
|
||||
timer = new CountDownTimer(minQrSpeed-Math.abs(qrDelay)+1, 1000) {
|
||||
public void onTick(long millisUntilFinished) {}
|
||||
public void onFinish() {
|
||||
updateQr();
|
||||
qrLoop();
|
||||
private boolean shouldstop = false;
|
||||
|
||||
private void startLoop() {
|
||||
final Handler handler = new Handler();
|
||||
Runnable runnable = new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if(shouldstop){
|
||||
return;
|
||||
}
|
||||
try{
|
||||
updateQr();
|
||||
}
|
||||
catch (Exception e) {
|
||||
AlertManager.error(e);
|
||||
}
|
||||
finally{
|
||||
double a = ((double) maxQrSpeed) / (Math.abs(qrDelay));
|
||||
a = Math.min(Math.max(a, maxQrSpeed), minQrSpeed);
|
||||
handler.postDelayed(this, (long) a);
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
};
|
||||
handler.post(runnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
shouldstop = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ public class CodeOverlayView extends View {
|
||||
}
|
||||
}
|
||||
if(barColors != null){
|
||||
final double width = getWidth()/barColors.length;
|
||||
final double width = (double) getWidth() /barColors.length;
|
||||
|
||||
final int top = 0;
|
||||
final int bottom = barHeight;
|
||||
|
||||
@@ -14,19 +14,69 @@ import com.google.zxing.Reader;
|
||||
import com.google.zxing.Result;
|
||||
import com.google.zxing.common.HybridBinarizer;
|
||||
import com.google.zxing.datamatrix.DataMatrixReader;
|
||||
import com.ridgebotics.ridgescout.utility.TaskRunner;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.Function;
|
||||
|
||||
// Background task for code scanning, to not slow down the scanner.
|
||||
public class CodeScanTask extends AsyncTask<String, String, String>{
|
||||
private Function<String, String> resultFunction = null;
|
||||
//public class CodeScanTask extends AsyncTask<String, String, String>{
|
||||
// private Function<String, String> resultFunction = null;
|
||||
// private Bitmap image;
|
||||
//
|
||||
// @Override
|
||||
// protected String doInBackground(String... str) {
|
||||
// if(image == null){return null;}
|
||||
//
|
||||
// int width = image.getWidth();
|
||||
// int height = image.getHeight();
|
||||
// int[] pixels = new int[width * height];
|
||||
// image.getPixels(pixels, 0, width, 0, 0, width, height);
|
||||
//
|
||||
// RGBLuminanceSource source = new RGBLuminanceSource(width, height, pixels);
|
||||
// BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));
|
||||
//
|
||||
// Map<DecodeHintType, Object> hints = new HashMap<>();
|
||||
// hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
|
||||
//// hints.put(DecodeHintType.PURE_BARCODE, Boolean.TRUE);
|
||||
// hints.put(DecodeHintType.POSSIBLE_FORMATS, EnumSet.of(BarcodeFormat.DATA_MATRIX));
|
||||
//
|
||||
// Reader reader = new DataMatrixReader();
|
||||
// try {
|
||||
// Result result = reader.decode(binaryBitmap, hints);
|
||||
// return result.getText();
|
||||
// } catch (NotFoundException | ChecksumException | FormatException e) {
|
||||
//// AlertManager.error(e);
|
||||
// }
|
||||
//
|
||||
// return null;
|
||||
// }
|
||||
// public void setImage(Bitmap image){this.image = image;}
|
||||
// public void onResult(Function<String, String> func) {
|
||||
// this.resultFunction = func;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// protected void onPostExecute(String result) {
|
||||
// super.onPostExecute(result);
|
||||
// if(resultFunction != null){
|
||||
// resultFunction.apply(result);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
public class CodeScanTask implements Callable<String> {
|
||||
private Bitmap image;
|
||||
|
||||
public CodeScanTask(Bitmap image) {
|
||||
this.image = image;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String doInBackground(String... str) {
|
||||
public String call() {
|
||||
if(image == null){return null;}
|
||||
|
||||
int width = image.getWidth();
|
||||
@@ -52,17 +102,4 @@ public class CodeScanTask extends AsyncTask<String, String, String>{
|
||||
|
||||
return null;
|
||||
}
|
||||
public void setImage(Bitmap image){this.image = image;}
|
||||
public void onResult(Function<String, String> func) {
|
||||
this.resultFunction = func;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(String result) {
|
||||
super.onPostExecute(result);
|
||||
if(resultFunction != null){
|
||||
resultFunction.apply(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+44
-37
@@ -6,6 +6,7 @@ import android.Manifest;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Matrix;
|
||||
import android.media.Image;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
@@ -38,11 +39,15 @@ import com.ridgebotics.ridgescout.utility.AlertManager;
|
||||
import com.ridgebotics.ridgescout.utility.BuiltByteParser;
|
||||
import com.ridgebotics.ridgescout.utility.FileEditor;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.ridgebotics.ridgescout.utility.TaskRunner;
|
||||
|
||||
import org.checkerframework.checker.units.qual.C;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
@@ -62,21 +67,12 @@ public class CodeScannerView extends Fragment {
|
||||
alert.create().show();
|
||||
}
|
||||
|
||||
|
||||
private float scale = 0;
|
||||
private final int downscale = 1;
|
||||
private LifecycleOwner lifecycle;
|
||||
|
||||
private void setImage(Bitmap bmp){
|
||||
if(scale == 0) {
|
||||
scale = ((float) binding.container.getWidth() / bmp.getWidth()) * ((float) 16 / 9);
|
||||
binding.scannerImage.setTranslationX(0);
|
||||
binding.scannerImage.setTranslationY(0);
|
||||
}
|
||||
scanQRCode(bmp);
|
||||
binding.scannerImage.setImageBitmap(bmp);
|
||||
binding.scannerThreshold.bringToFront();
|
||||
// alert("test", getChildCount()+"");
|
||||
}
|
||||
|
||||
// private Bitmap img
|
||||
@@ -99,37 +95,38 @@ public class CodeScannerView extends Fragment {
|
||||
final int height = image.getHeight();
|
||||
|
||||
int[] pixels = new int[width * height];
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
int L = levelMap[yBuffer.get() & 0xff];
|
||||
pixels[y * width + x] = 0xff000000 | (L << 16) | (L << 8) | L;
|
||||
}
|
||||
for (int i = 0; i < width*height; i++) {
|
||||
int L = levelMap[yBuffer.get(i) & 0xff];
|
||||
pixels[i] = 0xff000000 | (L << 16) | (L << 8) | L;
|
||||
}
|
||||
|
||||
Matrix matrix = new Matrix();
|
||||
|
||||
matrix.postRotate(90);
|
||||
|
||||
|
||||
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
|
||||
bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
|
||||
// Bitmap.rota
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
public void scanQRCode(Bitmap bitmap) {
|
||||
|
||||
CodeScanTask async = new CodeScanTask();
|
||||
async.setImage(bitmap);
|
||||
async.onResult(data -> {
|
||||
// CodeScanTask async = new CodeScanTask();
|
||||
new TaskRunner().executeAsync(new CodeScanTask(bitmap), data -> {
|
||||
if(data != null){
|
||||
// alert("test", ""+fileEditor.byteFromChar(data.charAt(0)));
|
||||
compileData(
|
||||
FileEditor.byteFromChar(data.charAt(0)),
|
||||
FileEditor.byteFromChar(data.charAt(1)),
|
||||
FileEditor.byteFromChar(data.charAt(2)),
|
||||
(FileEditor.byteFromChar(data.charAt(3))+1),
|
||||
data.substring(4)
|
||||
FileEditor.byteFromChar(data.charAt(0)),
|
||||
FileEditor.byteFromChar(data.charAt(1)),
|
||||
FileEditor.byteFromChar(data.charAt(2)),
|
||||
(FileEditor.byteFromChar(data.charAt(3))+1),
|
||||
data.substring(4)
|
||||
);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
async.execute();
|
||||
|
||||
|
||||
|
||||
// return contents;
|
||||
@@ -231,26 +228,23 @@ public class CodeScannerView extends Fragment {
|
||||
|
||||
void bindPreview(@NonNull ProcessCameraProvider cameraProvider) {
|
||||
|
||||
Preview preview = new Preview.Builder()
|
||||
.setTargetAspectRatio(AspectRatio.RATIO_16_9)
|
||||
.setTargetRotation(Surface.ROTATION_180)
|
||||
.build();
|
||||
Preview preview = new Preview.Builder().build();
|
||||
|
||||
CameraSelector cameraSelector = new CameraSelector.Builder()
|
||||
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
|
||||
|
||||
// .addCameraFilter(CameraFilters.NON)
|
||||
.build();
|
||||
|
||||
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
|
||||
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder()
|
||||
// .setTargetResolution(new Size(224, 224))
|
||||
.setOutputImageRotationEnabled(false)
|
||||
.setTargetAspectRatio(AspectRatio.RATIO_16_9)
|
||||
// .setTargetAspectRatio(AspectRatio.RATIO_16_9)
|
||||
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
|
||||
// .setOutputImageRotationEnabled(true)
|
||||
// .setTargetRotation(Surface.ROTATION_0)
|
||||
.build();
|
||||
|
||||
|
||||
imageAnalysis.setAnalyzer(executor, new ImageAnalysis.Analyzer() {
|
||||
@OptIn(markerClass = ExperimentalGetImage.class) @Override
|
||||
public void analyze(@NonNull ImageProxy image) {
|
||||
@@ -269,6 +263,7 @@ public class CodeScannerView extends Fragment {
|
||||
});
|
||||
|
||||
cameraProvider.unbindAll();
|
||||
// cameraProvider.ro
|
||||
|
||||
cameraProvider.bindToLifecycle(lifecycle,
|
||||
cameraSelector, imageAnalysis, preview);
|
||||
@@ -295,6 +290,7 @@ public class CodeScannerView extends Fragment {
|
||||
Log.i("title", ""+qrCount);
|
||||
barColors = new int[qrCount];
|
||||
prevQrIndex = qrIndex;
|
||||
qrScannedCount = 0;
|
||||
}
|
||||
|
||||
final boolean updated;
|
||||
@@ -313,8 +309,20 @@ public class CodeScannerView extends Fragment {
|
||||
|
||||
if(updated && qrScannedCount >= qrCount){
|
||||
|
||||
AlertManager.startLoading("Decoding data...");
|
||||
new TaskRunner().executeAsync(new CodeDecodeTask(), result -> {
|
||||
AlertManager.stopLoading();
|
||||
});
|
||||
|
||||
}
|
||||
prevQrIndex = qrIndex;
|
||||
}
|
||||
|
||||
private class CodeDecodeTask implements Callable<Void> {
|
||||
@Override
|
||||
public Void call() {
|
||||
String compiledString = "";
|
||||
for(int i=0;i<qrCount;i++){
|
||||
for(int i=0;i<qrDataArr.length;i++){
|
||||
compiledString += qrDataArr[i];
|
||||
}
|
||||
|
||||
@@ -342,9 +350,8 @@ public class CodeScannerView extends Fragment {
|
||||
}catch (Exception e){
|
||||
AlertManager.error(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
prevQrIndex = qrIndex;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import com.ridgebotics.ridgescout.types.data.RawDataType;
|
||||
@@ -208,7 +209,7 @@ public class CandlestickView extends View {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,7 @@ public class CustomSpinnerView extends LinearLayout {
|
||||
this.index = defaultOption;
|
||||
|
||||
if(defaultOption != -1)
|
||||
this.item.setText(options.get(defaultOption));
|
||||
this.item.setText("▼ " + options.get(defaultOption));
|
||||
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
|
||||
@@ -78,7 +78,7 @@ public class CustomSpinnerView extends LinearLayout {
|
||||
CustomSpinnerPopup popup = new CustomSpinnerPopup(getContext()).init(options, option -> {
|
||||
// dialog.();
|
||||
if(!isEnabled()) return;
|
||||
item.setText(option);
|
||||
item.setText("▼ " + option);
|
||||
index = options.indexOf(option);
|
||||
if(onClickListener != null) {
|
||||
onClickListener.onClick(option, options.indexOf(option));
|
||||
@@ -105,12 +105,12 @@ public class CustomSpinnerView extends LinearLayout {
|
||||
}
|
||||
|
||||
public void setOption(String option) {
|
||||
item.setText(option);
|
||||
item.setText("▼ " + option);
|
||||
index = options.indexOf(option);
|
||||
}
|
||||
|
||||
public void setOption(int index) {
|
||||
item.setText(options.get(index));
|
||||
item.setText("▼ " + options.get(index));
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.ridgebotics.ridgescout.ui.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
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);
|
||||
|
||||
int currentNightMode = getContext().getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
|
||||
switch (currentNightMode) {
|
||||
case Configuration.UI_MODE_NIGHT_NO:
|
||||
// Night mode is not active on device
|
||||
match_indicator_back_button.setColorFilter(Color.BLACK, PorterDuff.Mode.SRC_IN);
|
||||
match_indicator_next_button.setColorFilter(Color.BLACK, PorterDuff.Mode.SRC_IN);
|
||||
break;
|
||||
case Configuration.UI_MODE_NIGHT_YES:
|
||||
// Night mode is active on device
|
||||
match_indicator_back_button.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN);
|
||||
match_indicator_next_button.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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){
|
||||
// Set color of main background rectangle
|
||||
Drawable box_drawable = box.getBackground();
|
||||
box_drawable.mutate();
|
||||
box_drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
|
||||
|
||||
float[] hsv = new float[3];
|
||||
Color.colorToHSV(color,hsv);
|
||||
|
||||
int background_color = Color.HSVToColor(220, new float[]{
|
||||
hsv[0],
|
||||
Math.min(hsv[1], 0.75f),
|
||||
Math.min(hsv[2], 0.5f)
|
||||
});
|
||||
|
||||
// Set color of main background rectangle, slightly dimmer
|
||||
coloredBackground.setBackgroundColor(
|
||||
background_color
|
||||
);
|
||||
|
||||
Drawable left_drawable = match_indicator_back_button.getBackground();
|
||||
left_drawable.mutate();
|
||||
left_drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
|
||||
|
||||
Drawable right_drawable = match_indicator_next_button.getBackground();
|
||||
right_drawable.mutate();
|
||||
right_drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.ridgebotics.ridgescout.ui.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
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.ImageButton;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
|
||||
import com.ridgebotics.ridgescout.R;
|
||||
|
||||
// A view for displaying information about a team.
|
||||
public class PitScoutingIndicator extends RelativeLayout {
|
||||
public PitScoutingIndicator(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public PitScoutingIndicator(Context context) {
|
||||
super(context);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public TextView pit_indicator_username;
|
||||
public TextView pit_indicator_team_num;
|
||||
private ConstraintLayout box;
|
||||
private View coloredBackground;
|
||||
|
||||
public void init(Context context) {
|
||||
LayoutInflater.from(context).inflate(R.layout.view_pit_scouting_indicator, this, true);
|
||||
pit_indicator_username = findViewById(R.id.pit_indicator_username);
|
||||
pit_indicator_team_num = findViewById(R.id.pit_indicator_teamnum);
|
||||
|
||||
box = findViewById(R.id.pit_indicator_box);
|
||||
coloredBackground = findViewById(R.id.pit_indicator_background);
|
||||
|
||||
}
|
||||
|
||||
public void setUsername(String username){
|
||||
pit_indicator_username.setText(username);
|
||||
}
|
||||
|
||||
public void setTeamNum(int teamNum) {
|
||||
pit_indicator_team_num.setText(String.valueOf(teamNum));
|
||||
}
|
||||
|
||||
public void setColor(int color){
|
||||
// Set color of main background rectangle
|
||||
Drawable box_drawable = box.getBackground();
|
||||
box_drawable.mutate();
|
||||
box_drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
|
||||
|
||||
float[] hsv = new float[3];
|
||||
Color.colorToHSV(color,hsv);
|
||||
|
||||
int background_color = Color.HSVToColor(220, new float[]{
|
||||
hsv[0],
|
||||
Math.min(hsv[1], 0.75f),
|
||||
Math.min(hsv[2], 0.5f)
|
||||
});
|
||||
|
||||
// Set color of main background rectangle, slightly dimmer
|
||||
coloredBackground.setBackgroundColor(
|
||||
background_color
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package com.ridgebotics.ridgescout.ui.views;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class RecyclerAdapter<T> extends RecyclerView.Adapter<RecyclerHolder<T>> {
|
||||
private List<T> items;
|
||||
private final int layoutResId;
|
||||
private final RecyclerHolderFactory<T> viewHolderFactory;
|
||||
private RecyclerClickListener<T> onItemClickListener;
|
||||
|
||||
public RecyclerAdapter(int layoutResId, RecyclerHolderFactory<T> viewHolderFactory) {
|
||||
this.items = new ArrayList<>();
|
||||
this.layoutResId = layoutResId;
|
||||
this.viewHolderFactory = viewHolderFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecyclerHolder<T> onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(layoutResId, parent, false);
|
||||
return viewHolderFactory.createViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(RecyclerHolder<T> holder, int position) {
|
||||
T item = items.get(position);
|
||||
holder.bind(item, position);
|
||||
holder.setOnItemClickListener(item, onItemClickListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return items.size();
|
||||
}
|
||||
|
||||
// List management methods
|
||||
public void setItems(List<T> newItems) {
|
||||
this.items.clear();
|
||||
if (newItems != null) {
|
||||
this.items.addAll(newItems);
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void addItem(T item) {
|
||||
items.add(item);
|
||||
notifyItemInserted(items.size() - 1);
|
||||
}
|
||||
|
||||
public void addItem(int position, T item) {
|
||||
items.add(position, item);
|
||||
notifyItemInserted(position);
|
||||
}
|
||||
|
||||
public void removeItem(int position) {
|
||||
if (position >= 0 && position < items.size()) {
|
||||
items.remove(position);
|
||||
notifyItemRemoved(position);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeItem(T item) {
|
||||
int position = items.indexOf(item);
|
||||
if (position != -1) {
|
||||
removeItem(position);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateItem(int position, T item) {
|
||||
if (position >= 0 && position < items.size()) {
|
||||
items.set(position, item);
|
||||
notifyItemChanged(position);
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
int size = items.size();
|
||||
items.clear();
|
||||
notifyItemRangeRemoved(0, size);
|
||||
}
|
||||
|
||||
public T getItem(int position) {
|
||||
return items.get(position);
|
||||
}
|
||||
|
||||
public List<T> getItems() {
|
||||
return new ArrayList<>(items);
|
||||
}
|
||||
|
||||
public void setOnItemClickListener(RecyclerClickListener<T> listener) {
|
||||
this.onItemClickListener = listener;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.ridgebotics.ridgescout.ui.views;
|
||||
|
||||
public interface RecyclerClickListener<T> {
|
||||
void onItemClick(T item, int position);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.ridgebotics.ridgescout.ui.views;
|
||||
|
||||
import android.view.View;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public abstract class RecyclerHolder<T> extends RecyclerView.ViewHolder {
|
||||
public RecyclerHolder(View itemView) {
|
||||
super(itemView);
|
||||
}
|
||||
|
||||
public abstract void bind(T item, int position);
|
||||
|
||||
// Optional method for handling item clicks
|
||||
public void setOnItemClickListener(T item, RecyclerClickListener<T> listener) {
|
||||
if (listener != null) {
|
||||
itemView.setOnClickListener(v -> listener.onItemClick(item, getAdapterPosition()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.ridgebotics.ridgescout.ui.views;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
public interface RecyclerHolderFactory<T> {
|
||||
RecyclerHolder<T> createViewHolder(View itemView);
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package com.ridgebotics.ridgescout.ui.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import java.util.List;
|
||||
|
||||
public class RecyclerList<T> extends RecyclerView {
|
||||
private RecyclerAdapter<T> adapter;
|
||||
|
||||
public RecyclerList(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public RecyclerList(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
public RecyclerList(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
// Set default layout manager
|
||||
setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
|
||||
// Enable optimizations
|
||||
setHasFixedSize(true);
|
||||
setItemViewCacheSize(20);
|
||||
setDrawingCacheEnabled(true);
|
||||
setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
|
||||
}
|
||||
|
||||
// Setup method to configure the RecyclerView
|
||||
public RecyclerList<T> setup(int layoutResId, RecyclerHolderFactory<T> RecyclerHolderFactory) {
|
||||
adapter = new RecyclerAdapter<>(layoutResId, RecyclerHolderFactory);
|
||||
setAdapter(adapter);
|
||||
return this;
|
||||
}
|
||||
|
||||
// Layout manager convenience methods
|
||||
public RecyclerList<T> withLinearLayout() {
|
||||
setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
return this;
|
||||
}
|
||||
|
||||
public RecyclerList<T> withLinearLayout(int orientation) {
|
||||
setLayoutManager(new LinearLayoutManager(getContext(), orientation, false));
|
||||
return this;
|
||||
}
|
||||
|
||||
public RecyclerList<T> withGridLayout(int spanCount) {
|
||||
setLayoutManager(new GridLayoutManager(getContext(), spanCount));
|
||||
return this;
|
||||
}
|
||||
|
||||
public RecyclerList<T> withDivider() {
|
||||
DividerItemDecoration divider = new DividerItemDecoration(getContext(),
|
||||
DividerItemDecoration.VERTICAL);
|
||||
addItemDecoration(divider);
|
||||
return this;
|
||||
}
|
||||
|
||||
public RecyclerList<T> withItemClickListener(RecyclerClickListener<T> listener) {
|
||||
if (adapter != null) {
|
||||
adapter.setOnItemClickListener(listener);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// Data management methods
|
||||
public void setItems(List<T> items) {
|
||||
if (adapter != null) {
|
||||
adapter.setItems(items);
|
||||
}
|
||||
}
|
||||
|
||||
public void addItem(T item) {
|
||||
if (adapter != null) {
|
||||
adapter.addItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
public void addItem(int position, T item) {
|
||||
if (adapter != null) {
|
||||
adapter.addItem(position, item);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeItem(int position) {
|
||||
if (adapter != null) {
|
||||
adapter.removeItem(position);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeItem(T item) {
|
||||
if (adapter != null) {
|
||||
adapter.removeItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateItem(int position, T item) {
|
||||
if (adapter != null) {
|
||||
adapter.updateItem(position, item);
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
if (adapter != null) {
|
||||
adapter.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public T getItem(int position) {
|
||||
return adapter != null ? adapter.getItem(position) : null;
|
||||
}
|
||||
|
||||
public List<T> getItems() {
|
||||
return adapter != null ? adapter.getItems() : null;
|
||||
}
|
||||
|
||||
public RecyclerAdapter<T> getGenericAdapter() {
|
||||
return adapter;
|
||||
}
|
||||
}
|
||||
@@ -19,34 +19,37 @@ import com.ridgebotics.ridgescout.R;
|
||||
import com.ridgebotics.ridgescout.types.frcTeam;
|
||||
|
||||
// A view that acts as a row specifically to display a team and their icon in a list formmt.
|
||||
public class TeamListOption extends LinearLayout {
|
||||
public TeamListOption(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context);
|
||||
}
|
||||
public class TeamListOption extends RecyclerHolder<frcTeam> {
|
||||
|
||||
public TeamListOption(Context context) {
|
||||
super(context);
|
||||
init(context);
|
||||
}
|
||||
|
||||
// public TeamListOption(Context context, @Nullable AttributeSet attrs) {
|
||||
// super(context, attrs);
|
||||
// init(context);
|
||||
// }
|
||||
//
|
||||
// public TeamListOption(Context context) {
|
||||
// super(context);
|
||||
// init(context);
|
||||
// }
|
||||
//
|
||||
private TextView teamNumber;
|
||||
private TextView teamName;
|
||||
|
||||
private ImageView teamLogo;
|
||||
private ConstraintLayout box;
|
||||
private View coloredBackground;
|
||||
//
|
||||
public TeamListOption(View view) {
|
||||
super(view);
|
||||
// LayoutInflater.from(context).inflate(R.layout.view_team_option, this, true);
|
||||
|
||||
public void init(Context context) {
|
||||
LayoutInflater.from(context).inflate(R.layout.view_team_option, this, true);
|
||||
|
||||
teamNumber = findViewById(R.id.field_option_type);
|
||||
teamName = findViewById(R.id.field_option_name);
|
||||
teamLogo = findViewById(R.id.team_option_logo);
|
||||
teamNumber = view.findViewById(R.id.field_option_type);
|
||||
teamName = view.findViewById(R.id.field_option_name);
|
||||
teamLogo = view.findViewById(R.id.team_option_logo);
|
||||
|
||||
|
||||
box = findViewById(R.id.team_option_box);
|
||||
coloredBackground = findViewById(R.id.team_option_background);
|
||||
box = view.findViewById(R.id.team_option_box);
|
||||
coloredBackground = view.findViewById(R.id.team_option_background);
|
||||
}
|
||||
|
||||
public void setTeamNumber(int num){
|
||||
@@ -59,6 +62,7 @@ public class TeamListOption extends LinearLayout {
|
||||
|
||||
public void setTeamLogo(Bitmap bitmap){
|
||||
teamLogo.setImageBitmap(bitmap);
|
||||
showLogo();
|
||||
}
|
||||
|
||||
public void hideLogo(){
|
||||
@@ -68,7 +72,8 @@ public class TeamListOption extends LinearLayout {
|
||||
teamLogo.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
public void fromTeam(frcTeam team){
|
||||
@Override
|
||||
public void bind(frcTeam team, int position){
|
||||
setTeamNumber(team.teamNumber);
|
||||
setTeamName(team.teamName);
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.ridgebotics.ridgescout.utility;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.widget.Toast;
|
||||
@@ -113,4 +114,22 @@ public class AlertManager {
|
||||
});
|
||||
}
|
||||
|
||||
private static ProgressDialog loadingDialog;
|
||||
|
||||
public static void startLoading(String title){
|
||||
((Activity) context).runOnUiThread(() -> {
|
||||
if(loadingDialog != null && loadingDialog.isShowing())
|
||||
loadingDialog.dismiss();
|
||||
loadingDialog = ProgressDialog.show(context, title, "Please wait...");
|
||||
});
|
||||
}
|
||||
|
||||
public static void stopLoading(){
|
||||
((Activity) context).runOnUiThread(() -> {
|
||||
if (loadingDialog != null)
|
||||
loadingDialog.cancel();
|
||||
loadingDialog = null;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import android.graphics.Color;
|
||||
public class Colors {
|
||||
// Lists and stuff
|
||||
public static final int color_found = 0x7f00ff00;
|
||||
public static final int color_rescout = 0x7f0000ff;
|
||||
public static final int color_rescout = 0xff007fff;
|
||||
public static final int color_not_found = 0x7f7f0000;
|
||||
|
||||
|
||||
@@ -14,9 +14,9 @@ public class Colors {
|
||||
public static final int unfocused_background_color = 0x50118811;
|
||||
|
||||
|
||||
public static final int unsaved_color = 0x60ff0000;
|
||||
public static final int saved_color = 0x6000ff00;
|
||||
public static final int rescout_color = 0x600000ff;
|
||||
public static final int unsaved_color = 0xffaa0000;
|
||||
public static final int saved_color = 0xff00aa00;
|
||||
public static final int rescout_color = 0xff007fff;
|
||||
|
||||
|
||||
// Data graphs
|
||||
|
||||
@@ -100,7 +100,7 @@ public final class FileEditor {
|
||||
|
||||
|
||||
public static int byteFromChar(char c){
|
||||
byte[] bytes = (String.valueOf(c)).getBytes(Charset.defaultCharset());
|
||||
byte[] bytes = (String.valueOf(c)).getBytes(StandardCharsets.ISO_8859_1);
|
||||
return Byte.toUnsignedInt(bytes[0]);
|
||||
}
|
||||
|
||||
@@ -398,9 +398,10 @@ public final class FileEditor {
|
||||
Arrays.sort(filenames, (o1, o2) -> {
|
||||
try {
|
||||
if (!o1.contains("-") || !o2.contains("-"))
|
||||
return 0;
|
||||
return Integer.valueOf(o1.split("-")[1]).compareTo(Integer.valueOf(o2.split("-")[1]));
|
||||
return o2.compareTo(o1);
|
||||
return Integer.valueOf(o1.split("-")[1].split("\\.")[0]).compareTo(Integer.valueOf(o2.split("-")[1].split("\\.")[0]));
|
||||
} catch (Exception e) {
|
||||
AlertManager.error(e);
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,11 +25,24 @@ public class ImageRequestTask extends AsyncTask<String, Void, Bitmap> {
|
||||
try {
|
||||
URL url = new URL(src);
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
// We do a little bit of spoofing
|
||||
connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11");
|
||||
connection.setDoInput(true);
|
||||
connection.connect();
|
||||
InputStream input = connection.getInputStream();
|
||||
return BitmapFactory.decodeStream(input);
|
||||
} catch (FileNotFoundException e) {
|
||||
|
||||
int code = connection.getResponseCode();
|
||||
switch (code) {
|
||||
case 200:
|
||||
InputStream input = connection.getInputStream();
|
||||
return BitmapFactory.decodeStream(input);
|
||||
case 403:
|
||||
// AlertManager.error("Got 403, Going to https://www.thebluealliance.com/avatars may fix this");
|
||||
return null;
|
||||
default:
|
||||
AlertManager.error("Error downloading image " + src, "Got response code: " + code);
|
||||
return null;
|
||||
}
|
||||
} catch (FileNotFoundException e){
|
||||
return null;
|
||||
} catch (IOException e){
|
||||
AlertManager.error("Error downloading image " + src, e);
|
||||
|
||||
@@ -28,6 +28,7 @@ public class SettingsManager {
|
||||
public static final String BtUUIDKey = "bt_uuid";
|
||||
public static final String FTPEnabled = "ftp_enabled";
|
||||
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 EnableQuickAllianceChangeKey = "enable_quick_alliance_change";
|
||||
@@ -53,7 +54,8 @@ public class SettingsManager {
|
||||
hm.put(TeamsDataModeKey, 0);
|
||||
hm.put(BtUUIDKey, UUID.randomUUID().toString());
|
||||
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(EnableQuickAllianceChangeKey, 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 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 void setFTPSendMetaFiles(boolean bool){getEditor().putBoolean(FTPSendMetaFiles,bool).apply();}
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.ridgebotics.ridgescout.utility;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
// https://stackoverflow.com/questions/58767733/the-asynctask-api-is-deprecated-in-android-11-what-are-the-alternatives
|
||||
public class TaskRunner {
|
||||
private final Executor executor = Executors.newSingleThreadExecutor(); // change according to your requirements
|
||||
private final Handler handler = new Handler(Looper.getMainLooper());
|
||||
|
||||
public interface Callback<R> {
|
||||
void onComplete(R result);
|
||||
}
|
||||
|
||||
public <R> void executeAsync(Callable<R> callable, Callback<R> callback) {
|
||||
executor.execute(() -> {
|
||||
final R result;
|
||||
try {
|
||||
result = callable.call();
|
||||
handler.post(() -> {
|
||||
callback.onComplete(result);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
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>
|
||||
@@ -17,36 +17,23 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ScrollView
|
||||
<LinearLayout
|
||||
android:id="@+id/table"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingBottom="50dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TableLayout
|
||||
android:id="@+id/table"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.ridgebotics.ridgescout.ui.views.CustomSpinnerView
|
||||
android:id="@+id/data_type_dropdown"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</TableLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
<com.ridgebotics.ridgescout.ui.views.CustomSpinnerView
|
||||
android:id="@+id/data_type_dropdown"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -92,17 +92,6 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_next_match"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
android:text="TextView"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/text_name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_match_alliance"
|
||||
android:layout_width="wrap_content"
|
||||
@@ -122,6 +111,15 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/event_button" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/info_box"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/text_name" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
@@ -5,16 +5,28 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
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
|
||||
android:id="@+id/scrollView3"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="8dp"
|
||||
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
|
||||
android:id="@+id/MatchScoutArea"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="48dp">
|
||||
|
||||
@@ -25,16 +37,15 @@
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_margin="5dp"
|
||||
android:background="@drawable/border"
|
||||
android:padding="10dp"
|
||||
android:orientation="horizontal">
|
||||
android:orientation="horizontal"
|
||||
android:padding="10dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/scouting_notice_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
|
||||
android:text="Scouting Notice">
|
||||
</TextView>
|
||||
android:text="Scouting Notice"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@@ -46,75 +57,4 @@
|
||||
</LinearLayout>
|
||||
</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>
|
||||
@@ -4,54 +4,27 @@
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/pit_file_indicator"
|
||||
<com.ridgebotics.ridgescout.ui.views.PitScoutingIndicator
|
||||
android:id="@+id/pit_indicator"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="#60ff0000"
|
||||
android:layout_height="60dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pit_bar_team_num"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="4388"
|
||||
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/pitUsername"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Username"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/pit_bar_team_num"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent">
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/pitScoutArea"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="48dp">
|
||||
android:paddingTop="60dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/scouting_notice_box"
|
||||
@@ -60,16 +33,15 @@
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_margin="5dp"
|
||||
android:background="@drawable/border"
|
||||
android:padding="10dp"
|
||||
android:orientation="horizontal">
|
||||
android:orientation="horizontal"
|
||||
android:padding="10dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/scouting_notice_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
|
||||
android:text="Scouting Notice">
|
||||
</TextView>
|
||||
android:text="Scouting Notice"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"></TextView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
android:id="@+id/scrollView2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="60dp"
|
||||
android:fillViewport="true"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
@@ -18,55 +17,70 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TableLayout
|
||||
android:id="@+id/SettingsTable"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
</TableLayout>
|
||||
<TableLayout
|
||||
android:id="@+id/SettingsTable"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" />
|
||||
|
||||
|
||||
<!-- <Button-->
|
||||
<!-- android:id="@+id/scoutNoticeButton"-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:layout_weight="1"-->
|
||||
<!-- android:layout_margin="5dp"-->
|
||||
<!-- android:text="Edit Scout notice"-->
|
||||
<!-- android:textSize="16sp" />-->
|
||||
|
||||
<!-- <LinearLayout-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:orientation="horizontal">-->
|
||||
|
||||
<!-- <Button-->
|
||||
<!-- android:id="@+id/fieldsPitsButton"-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:layout_weight="1"-->
|
||||
<!-- android:layout_margin="5dp"-->
|
||||
<!-- android:text="Edit PIT Fields"-->
|
||||
<!-- android:textSize="16sp" />-->
|
||||
|
||||
<!-- <Button-->
|
||||
<!-- android:id="@+id/fieldsMatchesButton"-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:layout_weight="1"-->
|
||||
<!-- android:layout_margin="5dp"-->
|
||||
<!-- android:text="Edit MATCH Fields"-->
|
||||
<!-- android:textSize="16sp" />-->
|
||||
|
||||
<!-- </LinearLayout>-->
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
|
||||
<Button
|
||||
android:id="@+id/fieldsButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Fields"
|
||||
android:textSize="34sp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/scrollView2"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
<!-- <LinearLayout-->
|
||||
<!-- android:id="@+id/fieldsButtons"-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:layout_marginTop="1dp"-->
|
||||
<!-- android:gravity="center"-->
|
||||
<!-- android:orientation="horizontal"-->
|
||||
<!-- android:visibility="gone"-->
|
||||
<!-- app:layout_constraintEnd_toEndOf="parent"-->
|
||||
<!-- app:layout_constraintStart_toStartOf="parent"-->
|
||||
<!-- app:layout_constraintBottom_toTopOf="@+id/fieldsButton"-->
|
||||
<!-- tools:visibility="visible">-->
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/fieldsButtons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="1dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@+id/fieldsButton"
|
||||
tools:visibility="visible">
|
||||
|
||||
<Button
|
||||
android:id="@+id/fieldsPitsButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="5dp"
|
||||
android:text="Pits"
|
||||
android:textSize="34sp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/fieldsMatchesButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="5dp"
|
||||
android:text="Matches"
|
||||
android:textSize="34sp" />
|
||||
</LinearLayout>
|
||||
<!-- </LinearLayout>-->
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -14,8 +14,7 @@
|
||||
android:id="@+id/teams"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="48dp" />
|
||||
android:orientation="vertical" />
|
||||
</ScrollView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -7,77 +7,65 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/scannerColors"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginTop="15dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/scannerThreshold"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/scannerColors" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/scannerBrightness"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/scannerThreshold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/scannerColorsLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginBottom="-12dp"
|
||||
android:text="Posterize"
|
||||
app:layout_constraintBottom_toTopOf="@+id/scannerColors"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/scannerThresholdLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginBottom="-12dp"
|
||||
android:text="Exposure"
|
||||
app:layout_constraintBottom_toTopOf="@+id/scannerThreshold"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/scannerBrightnessLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginBottom="-12dp"
|
||||
android:text="Brightness"
|
||||
app:layout_constraintBottom_toTopOf="@+id/scannerBrightness"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/scannerImage"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:adjustViewBounds="true"
|
||||
android:rotation="90"
|
||||
android:scaleType="fitCenter"
|
||||
android:scaleX="1"
|
||||
android:scaleY="1"
|
||||
android:translationY="-40dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:context=".CodeScannerView" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="3dp"
|
||||
android:padding="3dp"
|
||||
android:background="@drawable/border"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
tools:layout_editor_absoluteX="3dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/scannerColorsLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Posterize"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"/>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/scannerColors"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginTop="15dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/scannerThresholdLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Exposure"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"/>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/scannerThreshold"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/scannerBrightnessLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Brightness"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"/>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/scannerBrightness"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,31 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/qrSizeSlider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginBottom="60dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/qrImage" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/qrSpeedSlider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginBottom="60dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/qrSizeSlider"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.971"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/qrImage"
|
||||
app:layout_constraintVertical_bias="0.93" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/qrImage"
|
||||
android:layout_width="match_parent"
|
||||
@@ -36,23 +15,48 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/qrSpeedText"
|
||||
android:layout_width="wrap_content"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:text="QR Speed"
|
||||
app:layout_constraintBottom_toTopOf="@+id/qrSpeedSlider"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
android:layout_margin="3dp"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/border"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/qrSizeText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:text="QR Size"
|
||||
app:layout_constraintBottom_toTopOf="@+id/qrSizeSlider"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/qrSpeedText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:text="QR Speed"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
|
||||
app:layout_constraintBottom_toTopOf="@+id/qrSpeedSlider" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/qrSpeedSlider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/qrSizeText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:text="QR Size"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6" />
|
||||
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/qrSizeSlider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
tools:layout_editor_absoluteY="135dp" />
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/qrIndexN"
|
||||
@@ -60,7 +64,8 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="0"
|
||||
app:layout_constraintEnd_toStartOf="@+id/qrIndexSlash"
|
||||
app:layout_constraintTop_toBottomOf="@+id/qrImage" />
|
||||
app:layout_constraintTop_toBottomOf="@+id/qrImage"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/qrIndexSlash"
|
||||
@@ -69,7 +74,8 @@
|
||||
android:text="/"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/qrImage" />
|
||||
app:layout_constraintTop_toBottomOf="@+id/qrImage"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/qrIndexD"
|
||||
@@ -77,6 +83,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="0"
|
||||
app:layout_constraintStart_toEndOf="@+id/qrIndexSlash"
|
||||
app:layout_constraintTop_toBottomOf="@+id/qrImage" />
|
||||
app:layout_constraintTop_toBottomOf="@+id/qrImage"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -8,9 +8,12 @@
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="3dp"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginLeft="3dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginRight="3dp"
|
||||
android:layout_marginBottom="3dp"
|
||||
android:background="@drawable/border"
|
||||
android:orientation="horizontal"
|
||||
tools:ignore="UselessParent">
|
||||
|
||||
<TextView
|
||||
@@ -18,8 +21,9 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="10dp"
|
||||
android:textSize="24sp"
|
||||
android:overlapAnchor="false"/>
|
||||
android:overlapAnchor="false"
|
||||
android:text="▼ Options"
|
||||
android:textSize="24sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -28,9 +32,9 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="9dp"
|
||||
android:layout_marginTop="-5dp"
|
||||
android:layout_marginTop="-6dp"
|
||||
android:paddingLeft="3dp"
|
||||
android:paddingRight="3dp"
|
||||
android:text="Test"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"/>
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1" />
|
||||
</RelativeLayout>
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
<?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"
|
||||
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"
|
||||
tools:visibility="gone" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/match_indicator_back_button"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:contentDescription="Back"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/triangle_left"
|
||||
android:text="Back"
|
||||
android:background="@drawable/border"
|
||||
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:contentDescription="Next"
|
||||
android:src="@drawable/triangle_right"
|
||||
android:text="Next"
|
||||
android:background="@drawable/border"
|
||||
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="58dp"
|
||||
android:text="Temp"
|
||||
android:textAlignment="center"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="@+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="58dp"
|
||||
android:text="Temp"
|
||||
android:textAlignment="center"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
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>
|
||||
@@ -0,0 +1,55 @@
|
||||
<?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/pit_indicator_box"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="5dp"
|
||||
android:background="@drawable/border"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<View
|
||||
android:id="@+id/pit_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"
|
||||
tools:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pit_indicator_username"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="58dp"
|
||||
android:text="Username"
|
||||
android:textAlignment="center"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pit_indicator_teamnum"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:text="4388"
|
||||
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" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</RelativeLayout>
|
||||
@@ -17,6 +17,8 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:text="0"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
|
||||
android:textSize="24sp" />
|
||||
|
||||
<Button
|
||||
|
||||
@@ -28,13 +28,15 @@
|
||||
android:id="@+id/team_option_logo"
|
||||
android:layout_width="54dp"
|
||||
android:layout_height="54dp"
|
||||
android:layout_margin="3dp"
|
||||
android:layout_margin="2dp"
|
||||
android:scaleType="fitXY"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:srcCompat="@drawable/ic_robologo" />
|
||||
tools:srcCompat="@drawable/ic_robologo"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/field_option_name"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
[versions]
|
||||
agp = "8.8.0"
|
||||
agp = "8.11.1"
|
||||
asynclayoutinflator = "1.1.0"
|
||||
junit = "4.13.2"
|
||||
junitVersion = "1.1.5"
|
||||
espressoCore = "3.5.1"
|
||||
@@ -10,11 +11,13 @@ lifecycleLivedataKtx = "2.6.1"
|
||||
lifecycleViewmodelKtx = "2.6.1"
|
||||
material3 = "1.3.1"
|
||||
navigationFragment = "2.6.0"
|
||||
navigationFragmentVersion = "2.8.9"
|
||||
navigationUi = "2.6.0"
|
||||
supportAnnotations = "28.0.0"
|
||||
preference = "1.2.1"
|
||||
|
||||
[libraries]
|
||||
asynclayoutinflator = { module = "androidx.asynclayoutinflator:asynclayoutinflator", version.ref = "asynclayoutinflator" }
|
||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||
ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
||||
espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|
||||
@@ -25,6 +28,7 @@ lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-lived
|
||||
lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" }
|
||||
material3 = { module = "androidx.compose.material3:material3", version.ref = "material3" }
|
||||
navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment", version.ref = "navigationFragment" }
|
||||
navigation-fragment-v289 = { module = "androidx.navigation:navigation-fragment", version.ref = "navigationFragmentVersion" }
|
||||
navigation-ui = { group = "androidx.navigation", name = "navigation-ui", version.ref = "navigationUi" }
|
||||
support-annotations = { group = "com.android.support", name = "support-annotations", version.ref = "supportAnnotations" }
|
||||
preference = { group = "androidx.preference", name = "preference", version.ref = "preference" }
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
#Sun Mar 24 10:48:55 MDT 2024
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
+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