From f281ff2f0af3be24d1a746368f7c1e2d49840430 Mon Sep 17 00:00:00 2001 From: Michael Mikovsky <77305074+Astatin3@users.noreply.github.com> Date: Fri, 1 Aug 2025 18:16:26 -0600 Subject: [PATCH] Fix #16 --- .../ridgescout/types/ColabArray.java | 175 +++++++++++ .../ridgescout/ui/data/FieldDataFragment.java | 2 +- .../ridgescout/ui/transfer/FTPSync.java | 272 ------------------ .../ridgescout/ui/transfer/HttpSync.java | 200 +++++++------ .../ridgescout/utility/DataManager.java | 32 +-- .../ridgescout/utility/FileEditor.java | 56 +++- .../ridgescout/utility/HttpGetFile.java | 16 +- .../ridgescout/utility/HttpPutFile.java | 11 +- 8 files changed, 361 insertions(+), 403 deletions(-) delete mode 100644 app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/FTPSync.java diff --git a/app/src/main/java/com/ridgebotics/ridgescout/types/ColabArray.java b/app/src/main/java/com/ridgebotics/ridgescout/types/ColabArray.java index dcf0f18..528c74e 100644 --- a/app/src/main/java/com/ridgebotics/ridgescout/types/ColabArray.java +++ b/app/src/main/java/com/ridgebotics/ridgescout/types/ColabArray.java @@ -1,4 +1,179 @@ package com.ridgebotics.ridgescout.types; +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 java.io.File; +import java.time.Instant; +import java.time.temporal.TemporalField; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.function.Predicate; + public class ColabArray { + private enum Action { + ADD, + REMOVE + } + + private List changelog = new ArrayList<>(); + + private void addChange(Diff change) { + this.changelog.add(change); + } + + private List getChangelog() { + return changelog; + } + + public void add(String item) { + Diff diff = new Diff(); + diff.action = Action.ADD; + diff.content = item; + diff.time = new Date(); + addChange(diff); + } + + public void remove(String item) { + Diff diff = new Diff(); + diff.action = Action.REMOVE; + diff.content = item; + diff.time = new Date(); + addChange(diff); + } + + public void remove(int index) { + remove(get().get(index)); + } + + private static class Diff { + public Action action; + public String content; + public Date time; + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + + if (obj.getClass() != this.getClass()) { + return false; + } + + Diff other = (Diff) obj; + + return other.action == this.action && + other.time.getTime() == this.time.getTime() && + other.content.equals(this.content); + } + } + + + + public byte[] encode() throws ByteBuilder.buildingException{ + ByteBuilder bb = new ByteBuilder(); + + for(Diff change : this.changelog){ + bb.addInt(change.action.ordinal()); + bb.addString(change.content); + bb.addLong(change.time.getTime()); + } + + + return bb.build(); + } + + public static ColabArray decode(byte[] bytes) throws BuiltByteParser.byteParsingExeption { + BuiltByteParser bbp = new BuiltByteParser(bytes); + List results = bbp.parse(); + + if(results.size() % 3 != 0){ + throw new BuiltByteParser.byteParsingExeption("Wrong amount of elements in ColabArray!"); + } + + ColabArray arr = new ColabArray(); + + for(int i = 0; i < results.size(); i += 3) { + Diff diff = new Diff(); + diff.action = Action.values()[(int) results.get(i).get()]; + diff.content = (String) results.get(i+1).get(); + diff.time = new Date((long) results.get(i+2).get()); + arr.addChange(diff); + } + + + return arr; + } + + public void append(ColabArray other) { + + List otherlog = other.getChangelog(); + + otherlog.removeIf(diff -> + this.changelog.contains(diff) + ); + + this.changelog.addAll(otherlog); + this.changelog = Arrays.asList(sort(this.changelog)); + } + + public void append(File other) { + byte[] bytes = FileEditor.readFile(other); + if(bytes == null) return; + try { + append(decode(bytes)); + } catch (BuiltByteParser.byteParsingExeption e) { + AlertManager.error("Failed to append ColabArray!", e); + } + } + + private static Diff[] sort(List changelog) { + Diff[] sorted = changelog.toArray(new Diff[0]); + + try { + Arrays.sort(sorted, (o1, o2) -> (int) (o1.time.getTime() - o2.time.getTime())); + } catch (Exception e){ + AlertManager.error(e); + } + + return sorted; + } + + public List get() { + List result = new ArrayList<>(); + + for(Diff change : changelog) { + switch (change.action) { + case ADD: + result.add(change.content); + break; + case REMOVE: + result.remove(change.content); + break; + } + } + + return result; + } + + public boolean contains(String item) { +// Diff[] sorted = sort(); + + for(int i = changelog.size()-1; i >= 0; i--) { + Diff change = changelog.get(i); + if(!change.content.equals(item)) continue; + return change.action == Action.ADD; + } + + return false; + } + + public int size() { + return get().size(); + } } diff --git a/app/src/main/java/com/ridgebotics/ridgescout/ui/data/FieldDataFragment.java b/app/src/main/java/com/ridgebotics/ridgescout/ui/data/FieldDataFragment.java index a646cb0..514285d 100644 --- a/app/src/main/java/com/ridgebotics/ridgescout/ui/data/FieldDataFragment.java +++ b/app/src/main/java/com/ridgebotics/ridgescout/ui/data/FieldDataFragment.java @@ -58,7 +58,7 @@ public class FieldDataFragment extends Fragment { for (int teamIndex = 0; teamIndex < event.teams.size(); teamIndex++) { int teamNum = event.teams.get(teamIndex).teamNumber; List filenames = new ArrayList<>(List.of(FileEditor.getMatchesByTeamNum(evcode, event.teams.get(teamIndex).teamNumber))); - filenames.removeAll(rescout_list); + filenames.removeAll(rescout_list.get()); ArrayList teamData = new ArrayList<>(); diff --git a/app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/FTPSync.java b/app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/FTPSync.java deleted file mode 100644 index 2186b9f..0000000 --- a/app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/FTPSync.java +++ /dev/null @@ -1,272 +0,0 @@ -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; -import com.ridgebotics.ridgescout.utility.FileEditor; -import com.ridgebotics.ridgescout.utility.SettingsManager; - -import org.apache.commons.net.ftp.FTP; -import org.apache.commons.net.ftp.FTPClient; -import org.apache.commons.net.ftp.FTPFile; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.net.InetAddress; -import java.util.Arrays; -import java.util.Date; -import java.util.HashMap; -import java.util.Iterator; -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/"; - public static final String timestampsFilename = "timestamps"; - - - - public static long lastSyncTime = 0; - private static Date curSyncTime; - 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(); - FTPSync ftpSync = new FTPSync(); - - curSyncTime = new Date(); - - ftpSync.start(); - } - - FTPClient ftpClient; - - private int upCount = 0; - private int downCount = 0; - - private void downloadFile(String remoteFile, File localFile) throws IOException { - try (FileOutputStream fos = new FileOutputStream(localFile)) { - ftpClient.retrieveFile(remoteBasePath + remoteFile, fos); - } - } - - private void uploadFile(File localFile) throws IOException { - try (FileInputStream fis = new FileInputStream(localFile)) { - ftpClient.storeFile(remoteBasePath + localFile.getName(), fis); - } - } - - private FTPFile findRemoteFile(FTPFile[] remoteFiles, String fileName) { - for (FTPFile file : remoteFiles) { - if (file.getName().equals(fileName)) { - return file; - } - } - return null; - } - - private Date getUtcTimestamp(FTPFile file) { - return file.getTimestamp().getTime(); - } - - private Date getLocalFileUtcTimestamp(File file) { - return new Date(file.lastModified()); - } - - private void setLocalFileTimestamp(File file, Date date) { - file.setLastModified(date.getTime()); - } - - - public void run() { - isRunning = true; - boolean sendMetaFiles = SettingsManager.getFTPSendMetaFiles(); - - // Meta files - List meta_string_array = Arrays.asList( - "matches.fields", - "pits.fields", - evcode+".eventdata" - ); - - try { - // Login to FTP - ftpClient = new FTPClient(); - InetAddress address = InetAddress.getByName(SettingsManager.getFTPServer()); - ftpClient.connect(address); - ftpClient.login("anonymous", null); - ftpClient.enterLocalPassiveMode(); - ftpClient.setFileType(FTP.BINARY_FILE_TYPE); - - File localDir = new File(baseDir); - File[] localFiles = localDir.listFiles(); - Map remoteTimestamps = getTimestamps(); - - // Loop through local files and send all that are more recent - if (localFiles != null) { - for (int i = 0; i < localFiles.length; i++) { - File localFile = localFiles[i]; - setUpdateIndicator("Uploading " + (i+1) + "/" + localFiles.length); - - if(localFile.isDirectory()) continue; - // Remove timestamts file - if(localFile.getName().equals(timestampsFilename)) continue; - // Remove meta files if the option is disabled - if(!sendMetaFiles && meta_string_array.contains(localFile.getName())) continue; - - Date remoteTimestamp = remoteTimestamps.get(localFile.getName()); - - Date localTimeStamp = getLocalFileUtcTimestamp(localFile); - - if (remoteTimestamp == null || after(localTimeStamp, remoteTimestamp)) { - uploadFile(localFile); - Log.i(getClass().toString(), "Uploaded" + localFile.getName()); - - setLocalFileTimestamp(localFile, curSyncTime); - remoteTimestamps.put(localFile.getName(), curSyncTime); - upCount++; - }else{ - Log.i(getClass().toString(), "Did not upload"); - } - } - } - - Set keySet = remoteTimestamps.keySet(); - Iterator keyIt = keySet.iterator(); - for (int i = 0; i < keySet.size(); i++) { - String remoteFile = keyIt.next(); - setUpdateIndicator("Downloading " + (i+1) + "/" + keySet.size()); - - File localFile = new File(baseDir, remoteFile); - if(remoteFile.equals(timestampsFilename)) continue; - // Remove meta files if the option is disabled - if(!sendMetaFiles && meta_string_array.contains(remoteFile)) continue; - -// Date t1 = getLocalFileUtcTimestamp(localFile); -// Date t2 = getUtcTimestamp(remoteFile); -//// -// System.out.println("- " + t1 + (t1.after(t2) ? ">" : "<") + t2); - - Date localTimeStamp = getLocalFileUtcTimestamp(localFile); - Date remoteTimestamp = remoteTimestamps.get(remoteFile); - - - - if (!localFile.exists() || (after(remoteTimestamp, localTimeStamp) && !localTimeStamp.equals(remoteTimestamp))) { - downloadFile(remoteFile, localFile); - - Log.i(getClass().toString(), "Downloaded " + localFile.getName()); - - 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{ - Log.i(getClass().toString(), "Did not download"); - } - } - - setTimestamps(remoteTimestamps); - - } catch (Exception e) { - AlertManager.error("Failed Syncing!", e); - onResult.onResult(true, upCount, downCount); - setUpdateIndicator("ERROR!"); - } finally { - onResult.onResult(false, upCount, downCount); - setUpdateIndicator("Finished"); - } - - isRunning = false; - } - - private boolean setTimestamps(Map 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 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 pa = bbp.parse(); - - Map 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<>(); - } - } -} diff --git a/app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/HttpSync.java b/app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/HttpSync.java index 2c86c79..9b42c19 100644 --- a/app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/HttpSync.java +++ b/app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/HttpSync.java @@ -5,8 +5,6 @@ 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; @@ -23,10 +21,8 @@ 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; @@ -130,22 +126,25 @@ public class HttpSync extends Thread { TransferFile remoteFile = findInFileArray(remoteFiles, localFile.filename); + boolean sendField = (sendMetaFiles || !(localFile.filename.endsWith(".fields"))); - if( - ( - sendMetaFiles || !( - localFile.filename.endsWith(".fields") - ) + boolean shouldUpload; + boolean special; - ) - && (remoteFile == null || - ( - !Objects.equals(localFile.checksum, remoteFile.checksum) && - after(localFile.updated, remoteFile.updated) - ) - )) { - uploadFile(localFile, serverIP, serverKey); -// await(); + if(remoteFile == null) { + shouldUpload = true; + special = false; + } + else { + boolean checksumsEqual = Objects.equals(localFile.checksum, remoteFile.checksum); + special = FileEditor.requiresSpecialInteraction(remoteFile.filename); + boolean after = after(localFile.updated, remoteFile.updated); + + shouldUpload = !checksumsEqual && (special || after); + } + + if(sendField && shouldUpload) { + uploadFile(localFile, serverIP, serverKey, special); Log.d(getClass().toString(), "LocalFile: " + localFile.filename + ", " + localFile.checksum + ", " + localFile.updated + ": Uploaded"); upCount++; }else { @@ -162,13 +161,19 @@ public class HttpSync extends Thread { 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) - ) - ) { + boolean shouldUpload; + + if(localFile == null) { + shouldUpload = true; + } else { + boolean checksumsEqual = !Objects.equals(localFile.checksum, remoteFile.checksum); + boolean after = after(remoteFile.updated, localFile.updated); + boolean datesEqual = !localFile.updated.equals(remoteFile.updated); + + shouldUpload = (!checksumsEqual && (after) && !datesEqual); + } + + if(shouldUpload) { downloadFile(remoteFile, serverIP); // await(); Log.d(getClass().toString(), "RemoteFile: " + remoteFile.filename + ", " + remoteFile.checksum + ", " + remoteFile.updated + ": Downloaded"); @@ -277,70 +282,60 @@ public class HttpSync extends Thread { await(); } - -// private boolean setTimestamps(Map 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 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 pa = bbp.parse(); -// -// Map 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) { + void uploadFile(TransferFile tf, String serverURL, String apiKey, boolean special) { 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) + + if(special) { + HttpGetFile getTask = new HttpGetFile(serverURL + "/api/" + tf.filename, new File(baseDir + tf.filename), (stream, error) -> { + if(error != null) { + AlertManager.error(error); + return; + } else if (stream == null) { + AlertManager.error("Output stream from download was null!"); + return; + } + + byte[] bytes = stream.toByteArray(); + + FileEditor.syncColabArray( + tf.filename, + FileEditor.readFile(tf.filename), + bytes + ); + + + HttpPutFile uploadTask = new HttpPutFile(serverURL + "/api/" + tf.filename, new File(baseDir + tf.filename), error2 -> { + if (error2 != null) + AlertManager.error(error2); + runningRequest.set(true); + }, new String[]{ + "api_key: " + apiKey, + ("modified: " + tf.updated.getTime()) + }); + + uploadTask.execute(); + + + + }); + + getTask.execute(); + + + } else { + + HttpPutFile uploadTask = new HttpPutFile(serverURL + "/api/" + tf.filename, new File(baseDir + tf.filename), error -> { + if (error != null) AlertManager.error(error); runningRequest.set(true); - } - }, new String[]{ - "api_key: " + apiKey, - ("modified: " + tf.updated.getTime()) - }); // Pass auth token if needed + }, new String[]{ + "api_key: " + apiKey, + ("modified: " + tf.updated.getTime()) + }); // Pass auth token if needed - uploadTask.execute(); - await(); + uploadTask.execute(); + await(); + } } @@ -350,17 +345,32 @@ public class HttpSync extends Thread { 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); - + HttpGetFile uploadTask = new HttpGetFile(serverURL + "/api/" + tf.filename, f, (stream, error) -> { + if(error != null) { + AlertManager.error(error); + return; + } else if (stream == null) { + AlertManager.error("Output stream from download was null!"); + return; } - }); // Pass auth token if needed + + byte[] bytes = stream.toByteArray(); + + if(FileEditor.requiresSpecialInteraction(tf.filename)) { + FileEditor.syncColabArray( + tf.filename, + FileEditor.readFile(tf.filename), + bytes + ); + } else { + FileEditor.writeFile(tf.filename, bytes); + } + + setLocalFileTimestamp(f, tf.updated); + + runningRequest.set(true); + + }); uploadTask.execute(); await(); diff --git a/app/src/main/java/com/ridgebotics/ridgescout/utility/DataManager.java b/app/src/main/java/com/ridgebotics/ridgescout/utility/DataManager.java index d4e353f..9f0d65e 100644 --- a/app/src/main/java/com/ridgebotics/ridgescout/utility/DataManager.java +++ b/app/src/main/java/com/ridgebotics/ridgescout/utility/DataManager.java @@ -2,6 +2,7 @@ package com.ridgebotics.ridgescout.utility; import com.ridgebotics.ridgescout.scoutingData.Fields; import com.ridgebotics.ridgescout.scoutingData.transfer.TransferType; +import com.ridgebotics.ridgescout.types.ColabArray; import com.ridgebotics.ridgescout.types.frcEvent; import com.ridgebotics.ridgescout.types.input.FieldType; @@ -62,34 +63,27 @@ public class DataManager { } } - public static List rescout_list = new ArrayList<>(); + public static ColabArray rescout_list = new ColabArray(); public static void reload_rescout_list(){ - if(!FileEditor.fileExist(evcode + ".rescout")) {rescout_list = new ArrayList<>(); return;} - byte[] file = FileEditor.readFile(evcode + ".rescout"); - if(file == null) {rescout_list = new ArrayList<>(); return;} + String filename = evcode + ".rescout"; + if(!FileEditor.fileExist(filename)) {rescout_list = new ColabArray(); return;} + byte[] file = FileEditor.readFile(filename); + if(file == null) {rescout_list = new ColabArray(); return;} try { - BuiltByteParser bbp = new BuiltByteParser(file); - rescout_list = new ArrayList<>(Arrays.asList((String[]) (bbp.parse().get(0).get()))); - + rescout_list = ColabArray.decode(file); } catch (Exception e){ - AlertManager.error("Error loading scout fields", e); - rescout_list = new ArrayList<>(); + AlertManager.error("Error loading rescouting list", e); + rescout_list = new ColabArray(); } } public static void save_rescout_list() { + String filename = evcode + ".rescout"; try { - if(rescout_list.size() == 0){ - FileEditor.deleteFile(evcode + ".rescout"); - return; - } - - ByteBuilder bb = new ByteBuilder(); - bb.addStringArray(rescout_list.toArray(new String[0])); - FileEditor.writeFile(evcode + ".rescout", bb.build()); + FileEditor.writeFile(filename, rescout_list.encode()); } catch (Exception e){ - AlertManager.error("Error saving scout fields", e); + AlertManager.error("Error saving rescouting list", e); } } @@ -106,7 +100,7 @@ public class DataManager { } catch (Exception e){ AlertManager.error("Error loading scout notice", e); - rescout_list = new ArrayList<>(); + scoutNotice = ""; } } diff --git a/app/src/main/java/com/ridgebotics/ridgescout/utility/FileEditor.java b/app/src/main/java/com/ridgebotics/ridgescout/utility/FileEditor.java index 429b2b9..9eff383 100644 --- a/app/src/main/java/com/ridgebotics/ridgescout/utility/FileEditor.java +++ b/app/src/main/java/com/ridgebotics/ridgescout/utility/FileEditor.java @@ -8,6 +8,7 @@ import static com.ridgebotics.ridgescout.utility.DataManager.pit_values; import android.content.Context; import com.ridgebotics.ridgescout.scoutingData.ScoutingDataWriter; +import com.ridgebotics.ridgescout.types.ColabArray; import com.ridgebotics.ridgescout.types.frcEvent; import com.ridgebotics.ridgescout.types.frcTeam; @@ -239,16 +240,19 @@ public final class FileEditor { // } - public static boolean writeFile(String filepath, byte[] data) { + return writeFile(new File(baseDir + filepath), data); + } + + public static boolean writeFile(File file, byte[] data) { try { - FileOutputStream output = new FileOutputStream(baseDir + filepath); + FileOutputStream output = new FileOutputStream(file.getPath()); output.write(data); output.close(); // Date d = new Date(); - new File(baseDir + filepath).setLastModified(new Date().getTime()); + file.setLastModified(new Date().getTime()); return true; } catch (IOException e) { @@ -285,10 +289,15 @@ public final class FileEditor { } public static byte[] readFile(String path){ - return readFileExact(baseDir + path); + return readFileExact(new File(baseDir + path)); } - public static byte[] readFileExact(String path){ - File file = new File(path); + + public static byte[] readFile(File path){ + return readFileExact(path); + } + + + public static byte[] readFileExact(File file){ int size = (int) file.length(); byte[] bytes = new byte[size]; try { @@ -296,9 +305,6 @@ public final class FileEditor { buf.read(bytes, 0, bytes.length); buf.close(); return bytes; - } catch (FileNotFoundException e) { - AlertManager.error(e); - return null; } catch (IOException e) { AlertManager.error(e); return null; @@ -480,5 +486,37 @@ public final class FileEditor { } return removeFiles; } + + public static boolean requiresSpecialInteraction(String name) { +// String name = file.getName(); + + if(!fileExist(name)) { + return false; + } + + if(name.endsWith(".rescout")) { + return true; + } + + return false; + } + + public static void syncColabArray(String filename, byte[] currentBytes, byte[] newBytes) { + if(!fileExist(filename)) { + return; + } + + try{ + if(filename.endsWith(".rescout")) { + ColabArray colabArrayCurrent = ColabArray.decode(currentBytes); + ColabArray colabArrayNew = ColabArray.decode(newBytes); + + colabArrayCurrent.append(colabArrayNew); + writeFile(filename, colabArrayCurrent.encode()); + } + } catch (Exception e) { + AlertManager.error("Failed to sync ColabArray!", e); + } + } } diff --git a/app/src/main/java/com/ridgebotics/ridgescout/utility/HttpGetFile.java b/app/src/main/java/com/ridgebotics/ridgescout/utility/HttpGetFile.java index bbc8b66..64549c3 100644 --- a/app/src/main/java/com/ridgebotics/ridgescout/utility/HttpGetFile.java +++ b/app/src/main/java/com/ridgebotics/ridgescout/utility/HttpGetFile.java @@ -8,11 +8,12 @@ import java.net.URL; public class HttpGetFile extends AsyncTask { public interface DownloadCallback { - void onResult(String error); + void onResult(ByteArrayOutputStream bytes, String error); } private String downloadUrl; private File destinationFile; + private ByteArrayOutputStream outputStream; private DownloadCallback callback; private String errorMessage; public HttpGetFile(String downloadUrl, File destinationFile, DownloadCallback callback) { @@ -23,9 +24,12 @@ public class HttpGetFile extends AsyncTask { @Override protected File doInBackground(Void... voids) { + return run(); + } + + public File run() { HttpURLConnection connection = null; InputStream inputStream = null; - FileOutputStream outputStream = null; try { URL url = new URL(downloadUrl); @@ -64,7 +68,7 @@ public class HttpGetFile extends AsyncTask { } } - outputStream = new FileOutputStream(destinationFile); + outputStream = new ByteArrayOutputStream(); byte[] buffer = new byte[8192]; long downloadedBytes = 0; @@ -87,6 +91,8 @@ public class HttpGetFile extends AsyncTask { } outputStream.flush(); + +// FileEditor.writeFile(destinationFile, outputStream.toByteArray()); // Log.d(TAG, "Download successful. File saved to: " + destinationFile.getAbsolutePath()); return destinationFile; @@ -104,7 +110,7 @@ public class HttpGetFile extends AsyncTask { @Override protected void onPostExecute(File result) { if (callback != null) { - callback.onResult(errorMessage); + callback.onResult(outputStream, errorMessage); } } @@ -112,7 +118,7 @@ public class HttpGetFile extends AsyncTask { protected void onCancelled() { deletePartialFile(); if (callback != null) { - callback.onResult("Download cancelled"); + callback.onResult(null, "Download cancelled"); } } diff --git a/app/src/main/java/com/ridgebotics/ridgescout/utility/HttpPutFile.java b/app/src/main/java/com/ridgebotics/ridgescout/utility/HttpPutFile.java index 73b3583..46161da 100644 --- a/app/src/main/java/com/ridgebotics/ridgescout/utility/HttpPutFile.java +++ b/app/src/main/java/com/ridgebotics/ridgescout/utility/HttpPutFile.java @@ -1,10 +1,12 @@ package com.ridgebotics.ridgescout.utility; +import android.annotation.SuppressLint; import android.os.AsyncTask; //import android.util.Log; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; +import java.util.concurrent.atomic.AtomicReference; public class HttpPutFile extends AsyncTask { @@ -27,8 +29,13 @@ public class HttpPutFile extends AsyncTask { this.headers = headers; } + @SuppressLint("WrongThread") @Override protected Boolean doInBackground(Void... voids) { + return run(); + } + + public boolean run() { HttpURLConnection connection = null; InputStream fileInputStream = null; OutputStream outputStream = null; @@ -49,8 +56,8 @@ public class HttpPutFile extends AsyncTask { 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 + connection.setConnectTimeout(5000); // 5 seconds + connection.setReadTimeout(10000); // 10 seconds for(int i = 0; i < headers.length; i++){ String[] split = headers[i].split(": ");