From fb0718c4ec764b5e71f0356e4a4f4d1441e7f7d4 Mon Sep 17 00:00:00 2001 From: Astatin3 <77305074+Astatin3@users.noreply.github.com> Date: Sun, 6 Oct 2024 16:06:43 -0600 Subject: [PATCH] Add FTP server --- 2024week0-1728149849985.scoutbundle | Bin 0 -> 8123 bytes README.md | 6 +- TODO.md | 4 +- app/build.gradle.kts | 4 +- .../ridgebotics/ridgescout/MainActivity.java | 4 + .../ridgescout/ui/transfer/FTPSync.java | 225 +++++++++--------- .../ui/transfer/TransferFragment.java | 38 +-- .../ui/transfer/codes/CodeScannerView.java | 6 + .../ridgescout/utility/fileEditor.java | 40 ++-- 9 files changed, 176 insertions(+), 151 deletions(-) create mode 100644 2024week0-1728149849985.scoutbundle diff --git a/2024week0-1728149849985.scoutbundle b/2024week0-1728149849985.scoutbundle new file mode 100644 index 0000000000000000000000000000000000000000..7927e5da48b03eef2f5640f3d3405761ba248dc9 GIT binary patch literal 8123 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 RD>YIlBOmDm0v?p?{vZ7z?}PvV literal 0 HcmV?d00001 diff --git a/README.md b/README.md index df164ef..c291264 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![Ridgescout](https://github.com/Team4388/ScoutingApp2025/blob/main/metadata/en-US/images/FeatureGraphic.png?raw=true) +![Ridgescout](https://github.com/Team4388/ScoutingApp2025/blob/main/metadata/en-US/images/featureGraphic.png?raw=true) [Get it on F-Droid0 ? n : -n; +// } + + private boolean toleranceCompareAfter(Date d1, Date d2){ + long diff = d1.getTime() - d2.getTime(); + System.out.println(diff); + return diff > timeTolerance; + } + + public void run() { try { - FTPClient ftpClient = new FTPClient(); +// localTimeZone = TimeZone.getDefault(); + ftpClient = new FTPClient(); InetAddress address = InetAddress.getByName(settingsManager.getFTPServer()); ftpClient.connect(address); ftpClient.login("anonymous", null); ftpClient.enterLocalPassiveMode(); ftpClient.setFileType(FTP.BINARY_FILE_TYPE); -// ftpClient.setFileTransferMode(FTP.BLOCK_TRANSFER_MODE) - FTPFile[] remoteFilestmp = ftpClient.listFiles(remoteBasePath); - String[] localFilestmp = fileEditor.getEventFiles(evcode); + File localDir = new File(baseDir); + File[] localFiles = localDir.listFiles(); + FTPFile[] remoteFiles = ftpClient.listFiles(remoteBasePath); - FileDate[] remoteFiles = new FileDate[remoteFilestmp.length]; - for(int i = 0; i < remoteFilestmp.length; i++){ -// System.out.println(remoteFilestmp[i].getName()); - remoteFiles[i] = new FileDate(); - remoteFiles[i].filename = remoteFilestmp[i].getName(); - System.out.println(remoteFiles[i].filename); - remoteFiles[i].lastModified = remoteFilestmp[i].getTimestamp(); - } + if (localFiles != null) { + for (File localFile : localFiles) { + if (localFile.isFile()) { + FTPFile remoteFile = findRemoteFile(remoteFiles, localFile.getName()); +// +// Date t1 = getLocalFileUtcTimestamp(localFile); +// Date t2 = getUtcTimestamp(remoteFile); +//// +// System.out.println("- " + t1.getTime() + (t1.after(t2) ? ">" : "<") + t2.getTime()); - FileDate[] localFiles = new FileDate[localFilestmp.length]; - for(int i = 0; i < localFilestmp.length; i++){ -// sendFile(localFilestmp[i]); - String filename = "matches.fields"; - File f = new File(baseDir + filename); -// File f = new File(baseDir + localFilestmp[i]); -// System.out.println(f.exists()); -// FileInputStream fin = new FileInputStream(f); -// System.out.println(f.getName() + ", " + f.exists() + ", " + sendFile(ftpClient, f)); -// fin.close(); - -// System.out.println(ftpClient.getStatus()); - - - localFiles[i] = new FileDate(); - localFiles[i].filename = localFilestmp[i]; - localFiles[i].lastModified = fileEditor.getLastModified(localFilestmp[i]); - } - - FileAction[] localActions = compareDates(localFiles, fd2map(remoteFiles), false); - System.out.println(localActions.length); - for(int i = 0 ; i < localActions.length; i++){ - - System.out.println(localActions[i].filename + ", " + localActions[i].action); - switch (localActions[i].action){ - case SEND: - sendFile(ftpClient, localActions[i].filename); - fileEditor.setLastModified(localActions[i].filename, localActions[i].otherTimestamp); - break; - case RECIEVE: - - break; + if (remoteFile == null || toleranceCompareAfter(getLocalFileUtcTimestamp(localFile), (getUtcTimestamp(remoteFile)))) { + uploadFile(localFile); + System.out.println("Uploaded " + localFile.getName()); + upCount++; + }else{ + System.out.println("Did not upload " + localFile.getName()); + } + } } } + for (FTPFile remoteFile : remoteFiles) { + if (!remoteFile.isDirectory()) { + File localFile = new File(baseDir, remoteFile.getName()); + +// Date t1 = getLocalFileUtcTimestamp(localFile); +// Date t2 = getUtcTimestamp(remoteFile); +//// +// System.out.println("- " + t1 + (t1.after(t2) ? ">" : "<") + t2); + + if (!localFile.exists() || toleranceCompareAfter(getUtcTimestamp(remoteFile), (getLocalFileUtcTimestamp(localFile)))) { + downloadFile(remoteFile, localFile); + System.out.println("Downloaded " + localFile.getName()); + downCount++; + }else{ + System.out.println("Did not download " + remoteFile.getName()); + } + } + } } catch (Exception e) { AlertManager.error(e); - onResult.onResult(true); - AlertManager.toast("Error Syncing!"); + onResult.onResult(true, upCount, downCount); + } finally { + onResult.onResult(false, upCount, downCount); } -// } finally { -// AlertManager.toast("Synced!"); -// onResult.onResult(false); -// } - } - - private Map fd2map(FileDate[] fdarr){ - Map map = new HashMap<>(); - for(int i = 0; i < fdarr.length; i++) - map.put(fdarr[i].filename, fdarr[i].lastModified); - return map; - } - - private enum SyncAction { - SEND, - RECIEVE, - NONE - } - - private class FileAction { - String filename; - SyncAction action; - Calendar otherTimestamp; - public FileAction(String filename, SyncAction action, Calendar otherTimestamp){ - this.filename = filename; - this.action = action; - this.otherTimestamp = otherTimestamp; - } - } - - private FileAction[] compareDates(FileDate[] files, Map refrence, boolean reverse){ - FileAction[] actions = new FileAction[files.length]; - for(int i = 0; i < files.length; i++){ - Calendar ref = refrence.get(files[i].filename); - - System.out.println(ref); - - if(ref == null || files[i].lastModified.after(ref)) { - actions[i] = new FileAction(files[i].filename, !reverse ? SyncAction.SEND : SyncAction.RECIEVE, ref); - }else if(files[i].lastModified.before(ref)){ - actions[i] = new FileAction(files[i].filename, !reverse ? SyncAction.RECIEVE : SyncAction.SEND, ref); - }else { - actions[i] = new FileAction(files[i].filename, SyncAction.NONE, ref); - } - } - return actions; } } diff --git a/app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/TransferFragment.java b/app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/TransferFragment.java index b116f65..b9abd4e 100644 --- a/app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/TransferFragment.java +++ b/app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/TransferFragment.java @@ -14,11 +14,14 @@ import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import com.ridgebotics.ridgescout.R; +import com.ridgebotics.ridgescout.utility.AlertManager; import com.ridgebotics.ridgescout.utility.settingsManager; import com.ridgebotics.ridgescout.databinding.FragmentTransferBinding; import com.ridgebotics.ridgescout.ui.transfer.bluetooth.BluetoothSenderFragment; import com.ridgebotics.ridgescout.ui.transfer.codes.CodeGeneratorView; +import java.util.Date; + public class TransferFragment extends Fragment { private FragmentTransferBinding binding; @@ -66,12 +69,31 @@ public class TransferFragment extends Fragment { alert.create().show(); }); + + if(!settingsManager.getWifiMode()) { + binding.TBAButton.setEnabled(false); + binding.SyncButton.setEnabled(false); + } + + if(!settingsManager.getFTPEnabled() || + new Date().getTime()-FTPSync.lastSyncTime < FTPSync.timeTolerance) { + binding.SyncButton.setEnabled(false); + } + + binding.SyncButton.setOnClickListener(v -> { + binding.SyncButton.setEnabled(false); + FTPSync.sync((error, upcount, downcount) -> getActivity().runOnUiThread(() -> { +// binding.SyncButton.setEnabled(true); + AlertManager.toast((!error ? "Synced! " : "Error Syncing. ") + upcount + " Up " + downcount + " Down"); + })); + }); + + if(evcode.equals("unset")){ binding.noEventError.setVisibility(View.VISIBLE); binding.uploadButton.setEnabled(false); binding.CSVButton.setEnabled(false); binding.downloadButton.setEnabled(true); - binding.SyncButton.setEnabled(false); return binding.getRoot(); } @@ -107,20 +129,6 @@ public class TransferFragment extends Fragment { builder.show(); }); - if(!settingsManager.getWifiMode()) { - binding.TBAButton.setEnabled(false); - binding.SyncButton.setEnabled(false); - } - - if(!settingsManager.getFTPEnabled()) { - binding.SyncButton.setEnabled(false); - } - - binding.SyncButton.setOnClickListener(v -> { - binding.SyncButton.setEnabled(false); - FTPSync.sync(error -> getActivity().runOnUiThread(() -> binding.SyncButton.setEnabled(true))); - }); - return binding.getRoot(); } diff --git a/app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/codes/CodeScannerView.java b/app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/codes/CodeScannerView.java index 71398c4..aecd20c 100644 --- a/app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/codes/CodeScannerView.java +++ b/app/src/main/java/com/ridgebotics/ridgescout/ui/transfer/codes/CodeScannerView.java @@ -2,7 +2,9 @@ package com.ridgebotics.ridgescout.ui.transfer.codes; import static androidx.core.math.MathUtils.clamp; +import android.Manifest; import android.app.AlertDialog; +import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.media.Image; import android.os.Bundle; @@ -25,6 +27,7 @@ import androidx.camera.core.ImageProxy; import androidx.camera.core.Preview; import androidx.camera.lifecycle.ProcessCameraProvider; import androidx.constraintlayout.widget.ConstraintLayout; +import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import androidx.lifecycle.LifecycleOwner; @@ -150,6 +153,9 @@ public class CodeScannerView extends Fragment { this.lifecycle = getViewLifecycleOwner(); + if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.CAMERA}, 1); + } uiHandler = new Handler(); 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 f8becf4..61f867d 100644 --- a/app/src/main/java/com/ridgebotics/ridgescout/utility/fileEditor.java +++ b/app/src/main/java/com/ridgebotics/ridgescout/utility/fileEditor.java @@ -15,6 +15,8 @@ import java.io.IOException; import java.nio.BufferOverflowException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; @@ -22,6 +24,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.List; +import java.util.TimeZone; import java.util.zip.DataFormatException; import java.util.zip.Deflater; import java.util.zip.Inflater; @@ -30,6 +33,7 @@ public final class fileEditor { public final static String baseDir = "/data/data/com.ridgebotics.ridgescout/"; public static final byte internalDataVersion = 0x01; public static final int maxCompressedBlockSize = 4096; +// private TimeZone localTimeZone = TimeZone.getDefault(); @@ -190,22 +194,22 @@ public final class fileEditor { - public static Calendar getLastModified(String filepath){ - File f = new File(baseDir + filepath); - if(f.exists()){ - Calendar calendar = Calendar.getInstance(); - calendar.setTimeInMillis(f.lastModified()); - return calendar; - } - return null; - } - - public static void setLastModified(String filepath, Calendar calendar){ - File f = new File(baseDir + filepath); - if(f.exists()){ - f.setLastModified(calendar.getTimeInMillis()); - } - } +// public static Calendar getLastModified(String filepath){ +// File f = new File(baseDir + filepath); +// if(f.exists()){ +// Calendar calendar = Calendar.getInstance(); +// calendar.setTimeInMillis(f.lastModified()); +// return calendar; +// } +// return null; +// } +// +// public static void setLastModified(String filepath, Calendar calendar){ +// File f = new File(baseDir + filepath); +// if(f.exists()){ +// f.setLastModified(calendar.getTimeInMillis()); +// } +// } @@ -214,6 +218,10 @@ public final class fileEditor { FileOutputStream output = new FileOutputStream(baseDir + filepath); output.write(data); output.close(); + +// Date d = new Date(); + + new File(baseDir + filepath).setLastModified(new Date().getTime()); return true; } catch (IOException e) {