From 35b01393e5b859f643162b3336245069755df28c Mon Sep 17 00:00:00 2001 From: mendhak Date: Thu, 14 Mar 2024 22:11:00 +0000 Subject: [PATCH 01/73] Understanding workmanager and what data can be passed along --- gpslogger/build.gradle | 7 +++- .../com/mendhak/gpslogger/common/Strings.java | 9 +++++ .../loggers/customurl/CustomUrlWorker.java | 37 +++++++++++++++++++ .../senders/customurl/CustomUrlManager.java | 31 ++++++++++++++-- 4 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java diff --git a/gpslogger/build.gradle b/gpslogger/build.gradle index feb2608b2..bf34997ce 100644 --- a/gpslogger/build.gradle +++ b/gpslogger/build.gradle @@ -37,7 +37,7 @@ repositories { } android { - compileSdkVersion 30 + compileSdkVersion 34 defaultConfig { applicationId "com.mendhak.gpslogger" @@ -149,6 +149,11 @@ dependencies { exclude group: 'com.google.android', module: 'android' } + //Android's WorkManager + implementation 'androidx.work:work-runtime:2.9.0' + // It needs Gson + implementation 'com.google.code.gson:gson:2.10.1' + //Event bus implementation 'de.greenrobot:eventbus:2.4.0' diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/common/Strings.java b/gpslogger/src/main/java/com/mendhak/gpslogger/common/Strings.java index d2152e5bc..83d058515 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/common/Strings.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/common/Strings.java @@ -22,6 +22,8 @@ import android.content.Context; import android.os.Build; + +import com.google.gson.Gson; import com.mendhak.gpslogger.BuildConfig; import com.mendhak.gpslogger.R; @@ -672,5 +674,12 @@ public String toString() { } + public static String serializeTojson(Object obj){ + return new Gson().toJson(obj); + } + + public static T deserializeFromJson(String json, Class clazz){ + return new Gson().fromJson(json, clazz); + } } diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java new file mode 100644 index 000000000..a7c972884 --- /dev/null +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java @@ -0,0 +1,37 @@ +package com.mendhak.gpslogger.loggers.customurl; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.work.Data; +import androidx.work.Worker; +import androidx.work.WorkerParameters; + +import com.mendhak.gpslogger.common.Strings; +import com.mendhak.gpslogger.common.slf4j.Logs; + +import org.slf4j.Logger; + +import java.util.ArrayList; + +public class CustomUrlWorker extends Worker { + + private static final Logger LOG = Logs.of(CustomUrlWorker.class); + public CustomUrlWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { + super(context, workerParams); + } + + @NonNull + @Override + public Result doWork() { + LOG.info("CustomUrlWorker doWork"); + Data data = getInputData(); + String[] urlRequests = data.getStringArray("urlRequests"); + CustomUrlRequest customUrlRequest = Strings.deserializeFromJson(urlRequests[0], CustomUrlRequest.class); + LOG.info(customUrlRequest.getLogURL()); + LOG.info(customUrlRequest.getHttpBody()); + LOG.info(customUrlRequest.getHttpHeaders().toString()); + LOG.info("CustomUrlWorker doWork data: " + data.toString()); + return Result.success(); + } +} diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java index c8e672f7e..917e53065 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java @@ -3,6 +3,11 @@ import android.location.Location; import android.os.Bundle; +import androidx.work.Data; +import androidx.work.OneTimeWorkRequest; +import androidx.work.WorkManager; +import androidx.work.WorkRequest; + import com.birbit.android.jobqueue.JobManager; import com.mendhak.gpslogger.common.AppSettings; import com.mendhak.gpslogger.common.BundleConstants; @@ -15,6 +20,7 @@ import com.mendhak.gpslogger.loggers.csv.CSVFileLogger; import com.mendhak.gpslogger.loggers.customurl.CustomUrlJob; import com.mendhak.gpslogger.loggers.customurl.CustomUrlRequest; +import com.mendhak.gpslogger.loggers.customurl.CustomUrlWorker; import com.mendhak.gpslogger.senders.FileSender; import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVRecord; @@ -179,11 +185,30 @@ private void sendLocations(SerializableLocation[] locations, File csvFile){ } public void sendByHttp(String url, String method, String body, String headers, String username, String password){ - JobManager jobManager = AppSettings.getJobManager(); +// JobManager jobManager = AppSettings.getJobManager(); +// CustomUrlRequest request = new CustomUrlRequest(url, method, +// body, headers, username, password); +// ArrayList requests = new ArrayList<>(Arrays.asList(request)); +// jobManager.addJobInBackground(new CustomUrlJob(requests, null, new UploadEvents.CustomUrl())); + + CustomUrlRequest request = new CustomUrlRequest(url, method, body, headers, username, password); - ArrayList requests = new ArrayList<>(Arrays.asList(request)); - jobManager.addJobInBackground(new CustomUrlJob(requests, null, new UploadEvents.CustomUrl())); +// ArrayList requests = new ArrayList<>(Arrays.asList(request)); + + String serializedRequest = Strings.serializeTojson(request); + + + + Data data = new Data.Builder() + .putStringArray("urlRequests", new String[]{serializedRequest}) + .putString("csvfile", null) + .build(); + WorkRequest workRequest = new OneTimeWorkRequest + .Builder(CustomUrlWorker.class) + .setInputData(data) + .build(); + WorkManager.getInstance(AppSettings.getInstance()).enqueue(workRequest); } private String getFormattedTextblock(String textToFormat, SerializableLocation loc) throws Exception { From 47069b909e75c5f5c723ee45e47fe2a7fdac2680 Mon Sep 17 00:00:00 2001 From: mendhak Date: Thu, 14 Mar 2024 22:18:03 +0000 Subject: [PATCH 02/73] Giving a work a tag, and that lets it be replaced/unique --- .../gpslogger/senders/customurl/CustomUrlManager.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java index 917e53065..bc8456e17 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java @@ -4,6 +4,7 @@ import android.os.Bundle; import androidx.work.Data; +import androidx.work.ExistingWorkPolicy; import androidx.work.OneTimeWorkRequest; import androidx.work.WorkManager; import androidx.work.WorkRequest; @@ -204,11 +205,11 @@ public void sendByHttp(String url, String method, String body, String headers, S .putStringArray("urlRequests", new String[]{serializedRequest}) .putString("csvfile", null) .build(); - WorkRequest workRequest = new OneTimeWorkRequest + OneTimeWorkRequest workRequest = new OneTimeWorkRequest .Builder(CustomUrlWorker.class) .setInputData(data) .build(); - WorkManager.getInstance(AppSettings.getInstance()).enqueue(workRequest); + WorkManager.getInstance(AppSettings.getInstance()).enqueueUniqueWork(serializedRequest, ExistingWorkPolicy.REPLACE, workRequest); } private String getFormattedTextblock(String textToFormat, SerializableLocation loc) throws Exception { From cda359dc5bee0fce7d71dac04928fcb11b2d4134 Mon Sep 17 00:00:00 2001 From: mendhak Date: Fri, 15 Mar 2024 18:58:07 +0000 Subject: [PATCH 03/73] Add network constraints, backoff criteria, initial delay, retry count --- gpslogger/build.gradle | 2 +- .../loggers/customurl/CustomUrlWorker.java | 17 +++++++++++++++++ .../senders/customurl/CustomUrlManager.java | 11 ++++++++++- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/gpslogger/build.gradle b/gpslogger/build.gradle index bf34997ce..da69dd8ad 100644 --- a/gpslogger/build.gradle +++ b/gpslogger/build.gradle @@ -45,7 +45,7 @@ android { targetSdkVersion 30 versionCode 130 - versionName "130-rc2" + versionName "130-workmgr" manifestPlaceholders = [ appAuthRedirectScheme: 'com.mendhak.gpslogger' diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java index a7c972884..d1528b8bd 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java @@ -7,12 +7,23 @@ import androidx.work.Worker; import androidx.work.WorkerParameters; +import com.mendhak.gpslogger.common.AppSettings; import com.mendhak.gpslogger.common.Strings; +import com.mendhak.gpslogger.common.network.Networks; import com.mendhak.gpslogger.common.slf4j.Logs; import org.slf4j.Logger; import java.util.ArrayList; +import java.util.Map; + +import javax.net.ssl.X509TrustManager; + +import de.greenrobot.event.EventBus; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; public class CustomUrlWorker extends Worker { @@ -32,6 +43,12 @@ public Result doWork() { LOG.info(customUrlRequest.getHttpBody()); LOG.info(customUrlRequest.getHttpHeaders().toString()); LOG.info("CustomUrlWorker doWork data: " + data.toString()); + + if(getRunAttemptCount() < 3){ + return Result.retry(); + } + + return Result.success(); } } diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java index bc8456e17..e3ca9c19a 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java @@ -3,6 +3,8 @@ import android.location.Location; import android.os.Bundle; +import androidx.work.BackoffPolicy; +import androidx.work.Constraints; import androidx.work.Data; import androidx.work.ExistingWorkPolicy; import androidx.work.OneTimeWorkRequest; @@ -205,11 +207,18 @@ public void sendByHttp(String url, String method, String body, String headers, S .putStringArray("urlRequests", new String[]{serializedRequest}) .putString("csvfile", null) .build(); + Constraints constraints = new Constraints.Builder() + .setRequiredNetworkType(androidx.work.NetworkType.CONNECTED) + .build(); OneTimeWorkRequest workRequest = new OneTimeWorkRequest .Builder(CustomUrlWorker.class) + .setConstraints(constraints) + .setInitialDelay(1, java.util.concurrent.TimeUnit.SECONDS) + .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 10, java.util.concurrent.TimeUnit.SECONDS) .setInputData(data) .build(); - WorkManager.getInstance(AppSettings.getInstance()).enqueueUniqueWork(serializedRequest, ExistingWorkPolicy.REPLACE, workRequest); + WorkManager.getInstance(AppSettings.getInstance()) + .enqueueUniqueWork(serializedRequest, ExistingWorkPolicy.REPLACE, workRequest); } private String getFormattedTextblock(String textToFormat, SerializableLocation loc) throws Exception { From 9c236ddafc4eb2d148fd67b51de05526ecaf0803 Mon Sep 17 00:00:00 2001 From: mendhak Date: Fri, 15 Mar 2024 21:10:23 +0000 Subject: [PATCH 04/73] Convert the Custom Url Job to a Custom Url Worker --- .../loggers/customurl/CustomUrlWorker.java | 91 ++++++++++++++++--- .../senders/customurl/CustomUrlManager.java | 49 +++++----- 2 files changed, 106 insertions(+), 34 deletions(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java index d1528b8bd..484e70af8 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java @@ -9,6 +9,7 @@ import com.mendhak.gpslogger.common.AppSettings; import com.mendhak.gpslogger.common.Strings; +import com.mendhak.gpslogger.common.events.UploadEvents; import com.mendhak.gpslogger.common.network.Networks; import com.mendhak.gpslogger.common.slf4j.Logs; @@ -35,20 +36,86 @@ public CustomUrlWorker(@NonNull Context context, @NonNull WorkerParameters worke @NonNull @Override public Result doWork() { - LOG.info("CustomUrlWorker doWork"); - Data data = getInputData(); - String[] urlRequests = data.getStringArray("urlRequests"); - CustomUrlRequest customUrlRequest = Strings.deserializeFromJson(urlRequests[0], CustomUrlRequest.class); - LOG.info(customUrlRequest.getLogURL()); - LOG.info(customUrlRequest.getHttpBody()); - LOG.info(customUrlRequest.getHttpHeaders().toString()); - LOG.info("CustomUrlWorker doWork data: " + data.toString()); - - if(getRunAttemptCount() < 3){ - return Result.retry(); + + String callbackType = getInputData().getString("callbackType"); + UploadEvents.BaseUploadEvent callbackEvent = new UploadEvents.CustomUrl(); + + if(callbackType.equals("opengts")){ + callbackEvent = new UploadEvents.OpenGTS(); + } + + String[] serializedRequests = getInputData().getStringArray("urlRequests"); + + CustomUrlRequest[] urlRequests = new CustomUrlRequest[serializedRequests.length]; + for (int i = 0; i < serializedRequests.length; i++) { + urlRequests[i] = Strings.deserializeFromJson(serializedRequests[i], CustomUrlRequest.class); + } + + boolean success = true; + String responseError = null; + String responseThrowableMessage = null; + + for (CustomUrlRequest urlRequest : urlRequests) { + try{ + LOG.info("HTTP Request - " + urlRequest.getLogURL()); + + OkHttpClient.Builder okBuilder = new OkHttpClient.Builder(); + okBuilder.sslSocketFactory(Networks.getSocketFactory(AppSettings.getInstance()), + (X509TrustManager) Networks.getTrustManager(AppSettings.getInstance())); + Request.Builder requestBuilder = new Request.Builder().url(urlRequest.getLogURL()); + + for (Map.Entry header : urlRequest.getHttpHeaders().entrySet()) { + requestBuilder.addHeader(header.getKey(), header.getValue()); + } + + if (!urlRequest.getHttpMethod().equalsIgnoreCase("GET")) { + RequestBody body = RequestBody.create(null, urlRequest.getHttpBody()); + requestBuilder = requestBuilder.method(urlRequest.getHttpMethod(), body); + } + + Request request = requestBuilder.build(); + Response response = okBuilder.build().newCall(request).execute(); + + if (response.isSuccessful()) { + LOG.debug("HTTP request complete with successful response code " + response); + } else { + LOG.error("HTTP request complete with unexpected response code " + response); + responseError = "Unexpected code " + response; + responseThrowableMessage = response.body().string(); + success = false; + } + + response.body().close(); + + if (!success) { + break; + } + } + catch (Exception e) { + LOG.error("Exception during Custom URL processing " + e); + responseError = "Exception " + e; + responseThrowableMessage = e.getMessage(); + success = false; + break; + } } + if(success) { + EventBus.getDefault().post(callbackEvent.succeeded()); + return Result.success(); + } + else { + if(getRunAttemptCount() < getRetryLimit()){ + LOG.warn(String.format("Custom URL: attempt %d failed, maximum %d attempts", getRunAttemptCount()+1, getRetryLimit())); + return Result.retry(); + } - return Result.success(); + EventBus.getDefault() + .post(callbackEvent.failed("Unexpected code " + responseError, new Throwable(responseThrowableMessage))); + return Result.failure(); + } + } + protected int getRetryLimit() { + return 3; } } diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java index e3ca9c19a..95f13da9b 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java @@ -9,19 +9,14 @@ import androidx.work.ExistingWorkPolicy; import androidx.work.OneTimeWorkRequest; import androidx.work.WorkManager; -import androidx.work.WorkRequest; - -import com.birbit.android.jobqueue.JobManager; import com.mendhak.gpslogger.common.AppSettings; import com.mendhak.gpslogger.common.BundleConstants; import com.mendhak.gpslogger.common.PreferenceHelper; import com.mendhak.gpslogger.common.SerializableLocation; import com.mendhak.gpslogger.common.Strings; import com.mendhak.gpslogger.common.Systems; -import com.mendhak.gpslogger.common.events.UploadEvents; import com.mendhak.gpslogger.common.slf4j.Logs; import com.mendhak.gpslogger.loggers.csv.CSVFileLogger; -import com.mendhak.gpslogger.loggers.customurl.CustomUrlJob; import com.mendhak.gpslogger.loggers.customurl.CustomUrlRequest; import com.mendhak.gpslogger.loggers.customurl.CustomUrlWorker; import com.mendhak.gpslogger.senders.FileSender; @@ -32,11 +27,11 @@ import java.io.FileReader; import java.io.Reader; import java.util.ArrayList; -import java.util.Arrays; import java.util.Date; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; public class CustomUrlManager extends FileSender { @@ -177,35 +172,45 @@ private void sendLocations(SerializableLocation[] locations, File csvFile){ } } - JobManager jobManager = AppSettings.getJobManager(); - jobManager.addJobInBackground( - new CustomUrlJob( - requests, - csvFile, - new UploadEvents.CustomUrl())); + String[] serializedRequests = new String[requests.size()]; + for (int i = 0; i < requests.size(); i++) { + serializedRequests[i] = Strings.serializeTojson(requests.get(i)); + } + + String tag = String.valueOf(Objects.hashCode(serializedRequests)); + + Data data = new Data.Builder() + .putStringArray("urlRequests", serializedRequests) + .putString("csvfile", csvFile.getAbsolutePath()) + .putString("callbackType", "customUrl") + .build(); + Constraints constraints = new Constraints.Builder() + .setRequiredNetworkType(androidx.work.NetworkType.CONNECTED) + .build(); + OneTimeWorkRequest workRequest = new OneTimeWorkRequest + .Builder(CustomUrlWorker.class) + .setConstraints(constraints) + .setInitialDelay(1, java.util.concurrent.TimeUnit.SECONDS) + .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, java.util.concurrent.TimeUnit.SECONDS) + .setInputData(data) + .build(); + WorkManager.getInstance(AppSettings.getInstance()) + .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); } } public void sendByHttp(String url, String method, String body, String headers, String username, String password){ -// JobManager jobManager = AppSettings.getJobManager(); -// CustomUrlRequest request = new CustomUrlRequest(url, method, -// body, headers, username, password); -// ArrayList requests = new ArrayList<>(Arrays.asList(request)); -// jobManager.addJobInBackground(new CustomUrlJob(requests, null, new UploadEvents.CustomUrl())); - CustomUrlRequest request = new CustomUrlRequest(url, method, body, headers, username, password); -// ArrayList requests = new ArrayList<>(Arrays.asList(request)); String serializedRequest = Strings.serializeTojson(request); - - Data data = new Data.Builder() .putStringArray("urlRequests", new String[]{serializedRequest}) .putString("csvfile", null) + .putString("callbackType", "customUrl") .build(); Constraints constraints = new Constraints.Builder() .setRequiredNetworkType(androidx.work.NetworkType.CONNECTED) @@ -214,7 +219,7 @@ public void sendByHttp(String url, String method, String body, String headers, S .Builder(CustomUrlWorker.class) .setConstraints(constraints) .setInitialDelay(1, java.util.concurrent.TimeUnit.SECONDS) - .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 10, java.util.concurrent.TimeUnit.SECONDS) + .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, java.util.concurrent.TimeUnit.SECONDS) .setInputData(data) .build(); WorkManager.getInstance(AppSettings.getInstance()) From cbd16565dabcd0cb6e7ab24613dcead65f899a23 Mon Sep 17 00:00:00 2001 From: mendhak Date: Fri, 15 Mar 2024 22:03:05 +0000 Subject: [PATCH 05/73] Sending on wifi vs any connection --- gpslogger/build.gradle | 2 ++ .../mendhak/gpslogger/senders/customurl/CustomUrlManager.java | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/gpslogger/build.gradle b/gpslogger/build.gradle index da69dd8ad..86d63c623 100644 --- a/gpslogger/build.gradle +++ b/gpslogger/build.gradle @@ -42,7 +42,9 @@ android { defaultConfig { applicationId "com.mendhak.gpslogger" minSdkVersion 16 + //noinspection ExpiredTargetSdkVersion targetSdkVersion 30 + compileSdk 34 versionCode 130 versionName "130-workmgr" diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java index 95f13da9b..badf819d0 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java @@ -7,6 +7,7 @@ import androidx.work.Constraints; import androidx.work.Data; import androidx.work.ExistingWorkPolicy; +import androidx.work.NetworkType; import androidx.work.OneTimeWorkRequest; import androidx.work.WorkManager; import com.mendhak.gpslogger.common.AppSettings; @@ -185,7 +186,7 @@ private void sendLocations(SerializableLocation[] locations, File csvFile){ .putString("callbackType", "customUrl") .build(); Constraints constraints = new Constraints.Builder() - .setRequiredNetworkType(androidx.work.NetworkType.CONNECTED) + .setRequiredNetworkType(preferenceHelper.shouldAutoSendOnWifiOnly() ? NetworkType.UNMETERED: NetworkType.CONNECTED) .build(); OneTimeWorkRequest workRequest = new OneTimeWorkRequest .Builder(CustomUrlWorker.class) From 121381240d7883bd5b8e990d9d25ba35d154f2ca Mon Sep 17 00:00:00 2001 From: mendhak Date: Sat, 16 Mar 2024 12:44:53 +0000 Subject: [PATCH 06/73] OpenGTS sender and logger to use the new WorkManager worker --- .../senders/opengts/OpenGTSManager.java | 37 ++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java index 39bb95afd..030ae1301 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java @@ -19,12 +19,21 @@ package com.mendhak.gpslogger.senders.opengts; +import androidx.work.BackoffPolicy; +import androidx.work.Constraints; +import androidx.work.Data; +import androidx.work.ExistingWorkPolicy; +import androidx.work.NetworkType; +import androidx.work.OneTimeWorkRequest; +import androidx.work.WorkManager; + import com.birbit.android.jobqueue.JobManager; import com.mendhak.gpslogger.common.*; import com.mendhak.gpslogger.common.events.UploadEvents; import com.mendhak.gpslogger.common.slf4j.Logs; import com.mendhak.gpslogger.loggers.customurl.CustomUrlJob; import com.mendhak.gpslogger.loggers.customurl.CustomUrlRequest; +import com.mendhak.gpslogger.loggers.customurl.CustomUrlWorker; import com.mendhak.gpslogger.loggers.opengts.OpenGtsUdpJob; import com.mendhak.gpslogger.senders.FileSender; import com.mendhak.gpslogger.senders.GpxReader; @@ -88,15 +97,33 @@ public void sendLocations(SerializableLocation[] locations){ void sendByHttp(String deviceId, String accountName, SerializableLocation[] locations, String communication, String path, String server, int port) { ArrayList requests = new ArrayList<>(); - JobManager jobManager = AppSettings.getJobManager(); - for(SerializableLocation loc:locations){ - String finalUrl = getUrl(deviceId, accountName, loc, communication, path, server, port, batteryLevel ); + String[] serializedRequests = new String[locations.length]; + for (int i = 0; i < locations.length; i++) { + String finalUrl = getUrl(deviceId, accountName, locations[i], communication, path, server, port, batteryLevel ); CustomUrlRequest request = new CustomUrlRequest(finalUrl); - requests.add(request); + serializedRequests[i] = Strings.serializeTojson(request); } - jobManager.addJobInBackground(new CustomUrlJob(requests, null, new UploadEvents.OpenGTS())); + + String tag = String.valueOf(Objects.hashCode(serializedRequests)); + Data data = new Data.Builder() + .putStringArray("urlRequests", serializedRequests) + .putString("callbackType", "opengts") + .build(); + Constraints constraints = new Constraints.Builder() + .setRequiredNetworkType(preferenceHelper.shouldAutoSendOnWifiOnly() ? NetworkType.UNMETERED: NetworkType.CONNECTED) + .build(); + OneTimeWorkRequest workRequest = new OneTimeWorkRequest + .Builder(CustomUrlWorker.class) + .setConstraints(constraints) + .setInitialDelay(1, java.util.concurrent.TimeUnit.SECONDS) + .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, java.util.concurrent.TimeUnit.SECONDS) + .setInputData(data) + .build(); + WorkManager.getInstance(AppSettings.getInstance()) + .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); + } From 180b366908a5e73064376ec786492284aea079ed Mon Sep 17 00:00:00 2001 From: mendhak Date: Sat, 16 Mar 2024 14:28:35 +0000 Subject: [PATCH 07/73] Custom URL Worker calls back to OpenGTS to build the requests --- .../loggers/customurl/CustomUrlWorker.java | 28 ++++++++-- .../senders/opengts/OpenGTSManager.java | 54 +++++++++++++++---- 2 files changed, 69 insertions(+), 13 deletions(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java index 484e70af8..a976c6645 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java @@ -8,14 +8,21 @@ import androidx.work.WorkerParameters; import com.mendhak.gpslogger.common.AppSettings; +import com.mendhak.gpslogger.common.PreferenceHelper; +import com.mendhak.gpslogger.common.SerializableLocation; import com.mendhak.gpslogger.common.Strings; import com.mendhak.gpslogger.common.events.UploadEvents; import com.mendhak.gpslogger.common.network.Networks; import com.mendhak.gpslogger.common.slf4j.Logs; +import com.mendhak.gpslogger.senders.GpxReader; +import com.mendhak.gpslogger.senders.opengts.OpenGTSManager; import org.slf4j.Logger; +import java.io.File; import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Map; import javax.net.ssl.X509TrustManager; @@ -44,11 +51,24 @@ public Result doWork() { callbackEvent = new UploadEvents.OpenGTS(); } - String[] serializedRequests = getInputData().getStringArray("urlRequests"); + CustomUrlRequest[] urlRequests; - CustomUrlRequest[] urlRequests = new CustomUrlRequest[serializedRequests.length]; - for (int i = 0; i < serializedRequests.length; i++) { - urlRequests[i] = Strings.deserializeFromJson(serializedRequests[i], CustomUrlRequest.class); + String gpxFilePath = getInputData().getString("gpxFilePath"); + if(!Strings.isNullOrEmpty(gpxFilePath)){ + OpenGTSManager openGTSManager = new OpenGTSManager(PreferenceHelper.getInstance()); + List gpxCustomUrlRequests = openGTSManager.getCustomUrlRequestsFromGPX(new File(gpxFilePath)); + urlRequests = gpxCustomUrlRequests.toArray(new CustomUrlRequest[0]); + } + else { + String[] serializedRequests = getInputData().getStringArray("urlRequests"); + if(serializedRequests == null){ + EventBus.getDefault().post(callbackEvent.failed("No URL requests found", new Throwable("No URL requests found"))); + return Result.failure(); + } + urlRequests = new CustomUrlRequest[serializedRequests.length]; + for (int i = 0; i < serializedRequests.length; i++) { + urlRequests[i] = Strings.deserializeFromJson(serializedRequests[i], CustomUrlRequest.class); + } } boolean success = true; diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java index 030ae1301..839921259 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java @@ -65,10 +65,23 @@ public void uploadFile(List files) { // Use only gpx for (File f : files) { if (f.getName().endsWith(".gpx")) { - List locations = getLocationsFromGPX(f); - LOG.debug(locations.size() + " points were read from " + f.getName()); - - sendLocations(locations.toArray(new SerializableLocation[locations.size()])); + String tag = String.valueOf(Objects.hashCode(f.getName())); + Data data = new Data.Builder() + .putString("gpxFilePath", f.getAbsolutePath()) + .putString("callbackType", "opengts") + .build(); + Constraints constraints = new Constraints.Builder() + .setRequiredNetworkType(preferenceHelper.shouldAutoSendOnWifiOnly() ? NetworkType.UNMETERED: NetworkType.CONNECTED) + .build(); + OneTimeWorkRequest workRequest = new OneTimeWorkRequest + .Builder(CustomUrlWorker.class) + .setConstraints(constraints) + .setInitialDelay(1, java.util.concurrent.TimeUnit.SECONDS) + .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, java.util.concurrent.TimeUnit.SECONDS) + .setInputData(data) + .build(); + WorkManager.getInstance(AppSettings.getInstance()) + .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); } } @@ -265,14 +278,37 @@ public String getName() { return SenderNames.OPENGTS; } - private List getLocationsFromGPX(File f) { - List locations = Collections.emptyList(); +// public List getLocationsFromGPX(File f) { +// List locations = Collections.emptyList(); +// try { +// locations = GpxReader.getPoints(f); +// } catch (Exception e) { +// LOG.error("OpenGTSManager.getLocationsFromGPX", e); +// } +// return locations; +// } + + public List getCustomUrlRequestsFromGPX(File f) { + List requests = new ArrayList<>(); try { - locations = GpxReader.getPoints(f); + List locations = GpxReader.getPoints(f); + LOG.debug(locations.size() + " points were read from " + f.getName()); + for (SerializableLocation location : locations) { + String finalUrl = getUrl(preferenceHelper.getOpenGTSDeviceId(), + preferenceHelper.getOpenGTSAccountName(), + location, + preferenceHelper.getOpenGTSServerCommunicationMethod(), + preferenceHelper.getOpenGTSServerPath(), + preferenceHelper.getOpenGTSServer(), + Integer.valueOf(preferenceHelper.getOpenGTSServerPort()), + batteryLevel); + CustomUrlRequest request = new CustomUrlRequest(finalUrl); + requests.add(request); + } } catch (Exception e) { - LOG.error("OpenGTSManager.getLocationsFromGPX", e); + LOG.error("OpenGTSManager.getCustomUrlRequestsFromGPX", e); } - return locations; + return requests; } @Override From 4ac67aca2e2bff10390b75fab98f0bdbbc128b33 Mon Sep 17 00:00:00 2001 From: mendhak Date: Sat, 16 Mar 2024 15:03:29 +0000 Subject: [PATCH 08/73] Ensure battery level is used in OpenGTS build --- .../mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java index a976c6645..d7dc8da97 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java @@ -11,6 +11,7 @@ import com.mendhak.gpslogger.common.PreferenceHelper; import com.mendhak.gpslogger.common.SerializableLocation; import com.mendhak.gpslogger.common.Strings; +import com.mendhak.gpslogger.common.Systems; import com.mendhak.gpslogger.common.events.UploadEvents; import com.mendhak.gpslogger.common.network.Networks; import com.mendhak.gpslogger.common.slf4j.Logs; @@ -55,7 +56,7 @@ public Result doWork() { String gpxFilePath = getInputData().getString("gpxFilePath"); if(!Strings.isNullOrEmpty(gpxFilePath)){ - OpenGTSManager openGTSManager = new OpenGTSManager(PreferenceHelper.getInstance()); + OpenGTSManager openGTSManager = new OpenGTSManager(PreferenceHelper.getInstance(), Systems.getBatteryInfo(AppSettings.getInstance()).BatteryLevel); List gpxCustomUrlRequests = openGTSManager.getCustomUrlRequestsFromGPX(new File(gpxFilePath)); urlRequests = gpxCustomUrlRequests.toArray(new CustomUrlRequest[0]); } From 90be8112125fa7e69d9f236d02544ba056a223c6 Mon Sep 17 00:00:00 2001 From: mendhak Date: Sat, 16 Mar 2024 15:55:13 +0000 Subject: [PATCH 09/73] Move Custom URL CSV upload into the Custom URL worker --- .../loggers/customurl/CustomUrlWorker.java | 11 ++-- .../senders/customurl/CustomUrlManager.java | 51 +++++++++++++++++-- .../senders/opengts/OpenGTSManager.java | 12 ----- 3 files changed, 55 insertions(+), 19 deletions(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java index d7dc8da97..dd0496c6b 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java @@ -9,20 +9,17 @@ import com.mendhak.gpslogger.common.AppSettings; import com.mendhak.gpslogger.common.PreferenceHelper; -import com.mendhak.gpslogger.common.SerializableLocation; import com.mendhak.gpslogger.common.Strings; import com.mendhak.gpslogger.common.Systems; import com.mendhak.gpslogger.common.events.UploadEvents; import com.mendhak.gpslogger.common.network.Networks; import com.mendhak.gpslogger.common.slf4j.Logs; -import com.mendhak.gpslogger.senders.GpxReader; +import com.mendhak.gpslogger.senders.customurl.CustomUrlManager; import com.mendhak.gpslogger.senders.opengts.OpenGTSManager; import org.slf4j.Logger; import java.io.File; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Map; @@ -55,11 +52,17 @@ public Result doWork() { CustomUrlRequest[] urlRequests; String gpxFilePath = getInputData().getString("gpxFilePath"); + String csvFilePath = getInputData().getString("csvFilePath"); if(!Strings.isNullOrEmpty(gpxFilePath)){ OpenGTSManager openGTSManager = new OpenGTSManager(PreferenceHelper.getInstance(), Systems.getBatteryInfo(AppSettings.getInstance()).BatteryLevel); List gpxCustomUrlRequests = openGTSManager.getCustomUrlRequestsFromGPX(new File(gpxFilePath)); urlRequests = gpxCustomUrlRequests.toArray(new CustomUrlRequest[0]); } + else if(!Strings.isNullOrEmpty(csvFilePath)){ + CustomUrlManager customUrlManager = new CustomUrlManager(PreferenceHelper.getInstance()); + List csvCustomUrlRequests = customUrlManager.getCustomUrlRequestsFromCSV(new File(csvFilePath)); + urlRequests = csvCustomUrlRequests.toArray(new CustomUrlRequest[0]); + } else { String[] serializedRequests = getInputData().getStringArray("urlRequests"); if(serializedRequests == null){ diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java index badf819d0..0ceb27cc0 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java @@ -49,9 +49,25 @@ public void uploadFile(List files) { for (File f : files) { if (f.getName().endsWith(".csv")) { foundFileToSend = true; - List locations = getLocationsFromCSV(f); - LOG.debug(locations.size() + " points were read from " + f.getName()); - sendLocations(locations.toArray(new SerializableLocation[locations.size()]), f); + + String tag = String.valueOf(Objects.hashCode(f.getName())); + Data data = new Data.Builder() + .putString("csvFilePath", f.getAbsolutePath()) + .putString("callbackType", "customUrl") + .build(); + Constraints constraints = new Constraints.Builder() + .setRequiredNetworkType(preferenceHelper.shouldAutoSendOnWifiOnly() ? NetworkType.UNMETERED: NetworkType.CONNECTED) + .build(); + OneTimeWorkRequest workRequest = new OneTimeWorkRequest + .Builder(CustomUrlWorker.class) + .setConstraints(constraints) + .setInitialDelay(1, java.util.concurrent.TimeUnit.SECONDS) + .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, java.util.concurrent.TimeUnit.SECONDS) + .setInputData(data) + .build(); + WorkManager.getInstance(AppSettings.getInstance()) + .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); + } } @@ -317,4 +333,33 @@ public String getName() { public boolean accept(File dir, String name) { return name.toLowerCase().contains(".csv"); } + + public List getCustomUrlRequestsFromCSV(File f) { + List locations = getLocationsFromCSV(f); + LOG.debug(locations.size() + " points were read from " + f.getName()); + + List requests = new ArrayList<>(); + + String customLoggingUrl = preferenceHelper.getCustomLoggingUrl(); + String httpBody = preferenceHelper.getCustomLoggingHTTPBody(); + String httpHeaders = preferenceHelper.getCustomLoggingHTTPHeaders(); + String httpMethod = preferenceHelper.getCustomLoggingHTTPMethod(); + + for(SerializableLocation loc: locations){ + try { + String finalUrl = getFormattedTextblock(customLoggingUrl, loc); + String finalBody = getFormattedTextblock(httpBody, loc); + String finalHeaders = getFormattedTextblock(httpHeaders, loc); + + requests.add(new CustomUrlRequest(finalUrl, httpMethod, + finalBody, finalHeaders, preferenceHelper.getCustomLoggingBasicAuthUsername(), + preferenceHelper.getCustomLoggingBasicAuthPassword())); + } catch (Exception e) { + LOG.error("Could not build the Custom URL to send", e); + } + } + + return requests; + + } } diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java index 839921259..001b690ea 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java @@ -29,9 +29,7 @@ import com.birbit.android.jobqueue.JobManager; import com.mendhak.gpslogger.common.*; -import com.mendhak.gpslogger.common.events.UploadEvents; import com.mendhak.gpslogger.common.slf4j.Logs; -import com.mendhak.gpslogger.loggers.customurl.CustomUrlJob; import com.mendhak.gpslogger.loggers.customurl.CustomUrlRequest; import com.mendhak.gpslogger.loggers.customurl.CustomUrlWorker; import com.mendhak.gpslogger.loggers.opengts.OpenGtsUdpJob; @@ -278,16 +276,6 @@ public String getName() { return SenderNames.OPENGTS; } -// public List getLocationsFromGPX(File f) { -// List locations = Collections.emptyList(); -// try { -// locations = GpxReader.getPoints(f); -// } catch (Exception e) { -// LOG.error("OpenGTSManager.getLocationsFromGPX", e); -// } -// return locations; -// } - public List getCustomUrlRequestsFromGPX(File f) { List requests = new ArrayList<>(); try { From 5596fa8acce5938c8ad0ae7036d908180c525f12 Mon Sep 17 00:00:00 2001 From: mendhak Date: Sat, 16 Mar 2024 15:56:46 +0000 Subject: [PATCH 10/73] Remove unsed data input --- .../mendhak/gpslogger/senders/customurl/CustomUrlManager.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java index 0ceb27cc0..82d5e8a88 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java @@ -198,7 +198,6 @@ private void sendLocations(SerializableLocation[] locations, File csvFile){ Data data = new Data.Builder() .putStringArray("urlRequests", serializedRequests) - .putString("csvfile", csvFile.getAbsolutePath()) .putString("callbackType", "customUrl") .build(); Constraints constraints = new Constraints.Builder() @@ -226,7 +225,6 @@ public void sendByHttp(String url, String method, String body, String headers, S Data data = new Data.Builder() .putStringArray("urlRequests", new String[]{serializedRequest}) - .putString("csvfile", null) .putString("callbackType", "customUrl") .build(); Constraints constraints = new Constraints.Builder() From d9c6be0eea19547ca7ee90ee4d17a21b5ff13ad8 Mon Sep 17 00:00:00 2001 From: mendhak Date: Sat, 16 Mar 2024 16:10:24 +0000 Subject: [PATCH 11/73] Remove unused code --- .../loggers/customurl/CustomUrlJob.java | 2 - .../senders/customurl/CustomUrlManager.java | 49 +------------------ .../senders/opengts/OpenGTSManager.java | 1 - 3 files changed, 1 insertion(+), 51 deletions(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlJob.java b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlJob.java index 5420ffe88..2a76ff198 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlJob.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlJob.java @@ -52,7 +52,6 @@ public class CustomUrlJob extends Job { private UploadEvents.BaseUploadEvent callbackEvent; - private File csvFile; private ArrayList urlRequests; public CustomUrlJob(ArrayList urlRequests, File csvFile, UploadEvents.BaseUploadEvent callbackEvent) { @@ -60,7 +59,6 @@ public CustomUrlJob(ArrayList urlRequests, File csvFile, Uploa this.callbackEvent = callbackEvent; this.urlRequests = urlRequests; - this.csvFile = csvFile; } @Override diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java index 82d5e8a88..20fafe3ea 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java @@ -166,55 +166,7 @@ private String unApplyDecimalComma(String recordValue) { return recordValue.replace(",","."); } - private void sendLocations(SerializableLocation[] locations, File csvFile){ - if(locations.length > 0){ - - ArrayList requests = new ArrayList<>(); - String customLoggingUrl = preferenceHelper.getCustomLoggingUrl(); - String httpBody = preferenceHelper.getCustomLoggingHTTPBody(); - String httpHeaders = preferenceHelper.getCustomLoggingHTTPHeaders(); - String httpMethod = preferenceHelper.getCustomLoggingHTTPMethod(); - - for(SerializableLocation loc: locations){ - try { - String finalUrl = getFormattedTextblock(customLoggingUrl, loc); - String finalBody = getFormattedTextblock(httpBody, loc); - String finalHeaders = getFormattedTextblock(httpHeaders, loc); - - requests.add(new CustomUrlRequest(finalUrl, httpMethod, - finalBody, finalHeaders, preferenceHelper.getCustomLoggingBasicAuthUsername(), - preferenceHelper.getCustomLoggingBasicAuthPassword())); - } catch (Exception e) { - LOG.error("Could not build the Custom URL to send", e); - } - } - - String[] serializedRequests = new String[requests.size()]; - for (int i = 0; i < requests.size(); i++) { - serializedRequests[i] = Strings.serializeTojson(requests.get(i)); - } - - String tag = String.valueOf(Objects.hashCode(serializedRequests)); - - Data data = new Data.Builder() - .putStringArray("urlRequests", serializedRequests) - .putString("callbackType", "customUrl") - .build(); - Constraints constraints = new Constraints.Builder() - .setRequiredNetworkType(preferenceHelper.shouldAutoSendOnWifiOnly() ? NetworkType.UNMETERED: NetworkType.CONNECTED) - .build(); - OneTimeWorkRequest workRequest = new OneTimeWorkRequest - .Builder(CustomUrlWorker.class) - .setConstraints(constraints) - .setInitialDelay(1, java.util.concurrent.TimeUnit.SECONDS) - .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, java.util.concurrent.TimeUnit.SECONDS) - .setInputData(data) - .build(); - WorkManager.getInstance(AppSettings.getInstance()) - .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); - } - } public void sendByHttp(String url, String method, String body, String headers, String username, String password){ @@ -360,4 +312,5 @@ public List getCustomUrlRequestsFromCSV(File f) { return requests; } + } diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java index 001b690ea..baaad3559 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java @@ -107,7 +107,6 @@ public void sendLocations(SerializableLocation[] locations){ } void sendByHttp(String deviceId, String accountName, SerializableLocation[] locations, String communication, String path, String server, int port) { - ArrayList requests = new ArrayList<>(); String[] serializedRequests = new String[locations.length]; for (int i = 0; i < locations.length; i++) { From e5d73c02b0bd3f10bcb79b0ac49e21de78814ec1 Mon Sep 17 00:00:00 2001 From: mendhak Date: Sat, 16 Mar 2024 16:32:33 +0000 Subject: [PATCH 12/73] Clarify run attempt count in log message --- .../mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java index dd0496c6b..5ac1e94c2 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java @@ -130,7 +130,7 @@ else if(!Strings.isNullOrEmpty(csvFilePath)){ } else { if(getRunAttemptCount() < getRetryLimit()){ - LOG.warn(String.format("Custom URL: attempt %d failed, maximum %d attempts", getRunAttemptCount()+1, getRetryLimit())); + LOG.warn(String.format("Custom URL: attempt %d failed, maximum %d attempts", getRunAttemptCount(), getRetryLimit())); return Result.retry(); } From 385a192e472c554a2f192f58c7025c3af7d15cab Mon Sep 17 00:00:00 2001 From: mendhak Date: Sat, 16 Mar 2024 17:03:27 +0000 Subject: [PATCH 13/73] Custom URL should respect the wifi-only setting --- .../mendhak/gpslogger/senders/customurl/CustomUrlManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java index 20fafe3ea..c3c44e0d1 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java @@ -180,7 +180,7 @@ public void sendByHttp(String url, String method, String body, String headers, S .putString("callbackType", "customUrl") .build(); Constraints constraints = new Constraints.Builder() - .setRequiredNetworkType(androidx.work.NetworkType.CONNECTED) + .setRequiredNetworkType(preferenceHelper.shouldAutoSendOnWifiOnly() ? NetworkType.UNMETERED: NetworkType.CONNECTED) .build(); OneTimeWorkRequest workRequest = new OneTimeWorkRequest .Builder(CustomUrlWorker.class) From c826b57d0636521b8ea43c2aedd6f51f9c4deaea Mon Sep 17 00:00:00 2001 From: mendhak Date: Sat, 16 Mar 2024 18:29:05 +0000 Subject: [PATCH 14/73] Refactor the workreqeust into a reusable method --- .../com/mendhak/gpslogger/common/Systems.java | 20 ++++++++++++++ .../senders/customurl/CustomUrlManager.java | 27 +++++-------------- .../senders/opengts/OpenGTSManager.java | 25 ++++------------- 3 files changed, 31 insertions(+), 41 deletions(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java b/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java index 7a5ff4b81..aa117eb67 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java @@ -39,6 +39,11 @@ import androidx.appcompat.app.AppCompatDelegate; import androidx.core.content.ContextCompat; import androidx.fragment.app.FragmentActivity; +import androidx.work.BackoffPolicy; +import androidx.work.Constraints; +import androidx.work.Data; +import androidx.work.NetworkType; +import androidx.work.OneTimeWorkRequest; import com.mendhak.gpslogger.common.slf4j.Logs; @@ -234,4 +239,19 @@ else if(appThemeSetting.equalsIgnoreCase("light")){ } + + public static OneTimeWorkRequest getBasicOneTimeWorkRequest(Class workerClass, Data data) { + + Constraints constraints = new Constraints.Builder() + .setRequiredNetworkType(PreferenceHelper.getInstance().shouldAutoSendOnWifiOnly() ? NetworkType.UNMETERED: NetworkType.CONNECTED) + .build(); + OneTimeWorkRequest workRequest = new OneTimeWorkRequest + .Builder(workerClass) + .setConstraints(constraints) + .setInitialDelay(1, java.util.concurrent.TimeUnit.SECONDS) + .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, java.util.concurrent.TimeUnit.SECONDS) + .setInputData(data) + .build(); + return workRequest; + } } diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java index c3c44e0d1..df62aad1a 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java @@ -55,16 +55,8 @@ public void uploadFile(List files) { .putString("csvFilePath", f.getAbsolutePath()) .putString("callbackType", "customUrl") .build(); - Constraints constraints = new Constraints.Builder() - .setRequiredNetworkType(preferenceHelper.shouldAutoSendOnWifiOnly() ? NetworkType.UNMETERED: NetworkType.CONNECTED) - .build(); - OneTimeWorkRequest workRequest = new OneTimeWorkRequest - .Builder(CustomUrlWorker.class) - .setConstraints(constraints) - .setInitialDelay(1, java.util.concurrent.TimeUnit.SECONDS) - .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, java.util.concurrent.TimeUnit.SECONDS) - .setInputData(data) - .build(); + + OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(CustomUrlWorker.class, data); WorkManager.getInstance(AppSettings.getInstance()) .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); @@ -174,23 +166,16 @@ public void sendByHttp(String url, String method, String body, String headers, S body, headers, username, password); String serializedRequest = Strings.serializeTojson(request); + String tag = String.valueOf(Objects.hashCode(serializedRequest)); Data data = new Data.Builder() .putStringArray("urlRequests", new String[]{serializedRequest}) .putString("callbackType", "customUrl") .build(); - Constraints constraints = new Constraints.Builder() - .setRequiredNetworkType(preferenceHelper.shouldAutoSendOnWifiOnly() ? NetworkType.UNMETERED: NetworkType.CONNECTED) - .build(); - OneTimeWorkRequest workRequest = new OneTimeWorkRequest - .Builder(CustomUrlWorker.class) - .setConstraints(constraints) - .setInitialDelay(1, java.util.concurrent.TimeUnit.SECONDS) - .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, java.util.concurrent.TimeUnit.SECONDS) - .setInputData(data) - .build(); + + OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(CustomUrlWorker.class, data); WorkManager.getInstance(AppSettings.getInstance()) - .enqueueUniqueWork(serializedRequest, ExistingWorkPolicy.REPLACE, workRequest); + .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); } private String getFormattedTextblock(String textToFormat, SerializableLocation loc) throws Exception { diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java index baaad3559..9d4582930 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java @@ -68,16 +68,8 @@ public void uploadFile(List files) { .putString("gpxFilePath", f.getAbsolutePath()) .putString("callbackType", "opengts") .build(); - Constraints constraints = new Constraints.Builder() - .setRequiredNetworkType(preferenceHelper.shouldAutoSendOnWifiOnly() ? NetworkType.UNMETERED: NetworkType.CONNECTED) - .build(); - OneTimeWorkRequest workRequest = new OneTimeWorkRequest - .Builder(CustomUrlWorker.class) - .setConstraints(constraints) - .setInitialDelay(1, java.util.concurrent.TimeUnit.SECONDS) - .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, java.util.concurrent.TimeUnit.SECONDS) - .setInputData(data) - .build(); + + OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(CustomUrlWorker.class, data); WorkManager.getInstance(AppSettings.getInstance()) .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); @@ -121,19 +113,12 @@ void sendByHttp(String deviceId, String accountName, SerializableLocation[] loca .putStringArray("urlRequests", serializedRequests) .putString("callbackType", "opengts") .build(); - Constraints constraints = new Constraints.Builder() - .setRequiredNetworkType(preferenceHelper.shouldAutoSendOnWifiOnly() ? NetworkType.UNMETERED: NetworkType.CONNECTED) - .build(); - OneTimeWorkRequest workRequest = new OneTimeWorkRequest - .Builder(CustomUrlWorker.class) - .setConstraints(constraints) - .setInitialDelay(1, java.util.concurrent.TimeUnit.SECONDS) - .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, java.util.concurrent.TimeUnit.SECONDS) - .setInputData(data) - .build(); + + OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(CustomUrlWorker.class, data); WorkManager.getInstance(AppSettings.getInstance()) .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); + } From 21abbfbba669a814da37017680909d271f74e387 Mon Sep 17 00:00:00 2001 From: mendhak Date: Sat, 16 Mar 2024 21:04:42 +0000 Subject: [PATCH 15/73] Pass input data as a hashmap in the standard worker builder --- .../com/mendhak/gpslogger/common/Systems.java | 15 +++- .../loggers/customurl/CustomUrlWorker.java | 74 +++++++++++-------- .../senders/customurl/CustomUrlManager.java | 26 +++---- .../senders/opengts/OpenGTSManager.java | 26 +++---- 4 files changed, 82 insertions(+), 59 deletions(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java b/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java index aa117eb67..15c29846c 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java @@ -55,6 +55,7 @@ import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -239,8 +240,20 @@ else if(appThemeSetting.equalsIgnoreCase("light")){ } + /** + * Returns a OneTimeWorkRequest with the given worker class and data map. The constraints are set to + * UNMETERED network type if the user has set the app to only send on wifi. Otherwise it is set to + * CONNECTED. The initial delay is set to 1 second to avoid the work being enqueued immediately. + * The backoff criteria is set to exponential with a 30 second initial delay. + * @param workerClass + * @param dataMap + * @return + */ + public static OneTimeWorkRequest getBasicOneTimeWorkRequest(Class workerClass, HashMap dataMap) { - public static OneTimeWorkRequest getBasicOneTimeWorkRequest(Class workerClass, Data data) { + androidx.work.Data.Builder dataBuilder = new Data.Builder(); + dataBuilder.putAll(dataMap); + Data data = dataBuilder.build(); Constraints constraints = new Constraints.Builder() .setRequiredNetworkType(PreferenceHelper.getInstance().shouldAutoSendOnWifiOnly() ? NetworkType.UNMETERED: NetworkType.CONNECTED) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java index 5ac1e94c2..3b89ed17a 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java @@ -42,37 +42,12 @@ public CustomUrlWorker(@NonNull Context context, @NonNull WorkerParameters worke @Override public Result doWork() { - String callbackType = getInputData().getString("callbackType"); - UploadEvents.BaseUploadEvent callbackEvent = new UploadEvents.CustomUrl(); + UploadEvents.BaseUploadEvent callbackEvent = getCallbackEvent(); + CustomUrlRequest[] urlRequests = getCustomUrlRequests(getInputData()); - if(callbackType.equals("opengts")){ - callbackEvent = new UploadEvents.OpenGTS(); - } - - CustomUrlRequest[] urlRequests; - - String gpxFilePath = getInputData().getString("gpxFilePath"); - String csvFilePath = getInputData().getString("csvFilePath"); - if(!Strings.isNullOrEmpty(gpxFilePath)){ - OpenGTSManager openGTSManager = new OpenGTSManager(PreferenceHelper.getInstance(), Systems.getBatteryInfo(AppSettings.getInstance()).BatteryLevel); - List gpxCustomUrlRequests = openGTSManager.getCustomUrlRequestsFromGPX(new File(gpxFilePath)); - urlRequests = gpxCustomUrlRequests.toArray(new CustomUrlRequest[0]); - } - else if(!Strings.isNullOrEmpty(csvFilePath)){ - CustomUrlManager customUrlManager = new CustomUrlManager(PreferenceHelper.getInstance()); - List csvCustomUrlRequests = customUrlManager.getCustomUrlRequestsFromCSV(new File(csvFilePath)); - urlRequests = csvCustomUrlRequests.toArray(new CustomUrlRequest[0]); - } - else { - String[] serializedRequests = getInputData().getStringArray("urlRequests"); - if(serializedRequests == null){ - EventBus.getDefault().post(callbackEvent.failed("No URL requests found", new Throwable("No URL requests found"))); - return Result.failure(); - } - urlRequests = new CustomUrlRequest[serializedRequests.length]; - for (int i = 0; i < serializedRequests.length; i++) { - urlRequests[i] = Strings.deserializeFromJson(serializedRequests[i], CustomUrlRequest.class); - } + if(urlRequests == null || urlRequests.length == 0){ + EventBus.getDefault().post(callbackEvent.failed("Nothing to process", new Throwable("Nothing to process"))); + return Result.failure(); } boolean success = true; @@ -130,7 +105,7 @@ else if(!Strings.isNullOrEmpty(csvFilePath)){ } else { if(getRunAttemptCount() < getRetryLimit()){ - LOG.warn(String.format("Custom URL: attempt %d failed, maximum %d attempts", getRunAttemptCount(), getRetryLimit())); + LOG.warn(String.format("Custom URL - attempt %d of %d failed, will retry", getRunAttemptCount(), getRetryLimit())); return Result.retry(); } @@ -139,6 +114,43 @@ else if(!Strings.isNullOrEmpty(csvFilePath)){ return Result.failure(); } } + + private CustomUrlRequest[] getCustomUrlRequests(Data inputData) { + CustomUrlRequest[] urlRequests = null; + String[] serializedRequests = inputData.getStringArray("urlRequests"); + String gpxFilePath = inputData.getString("gpxFilePath"); + String csvFilePath = inputData.getString("csvFilePath"); + + if(!Strings.isNullOrEmpty(gpxFilePath)){ + OpenGTSManager openGTSManager = new OpenGTSManager(PreferenceHelper.getInstance(), Systems.getBatteryInfo(AppSettings.getInstance()).BatteryLevel); + List gpxCustomUrlRequests = openGTSManager.getCustomUrlRequestsFromGPX(new File(gpxFilePath)); + urlRequests = gpxCustomUrlRequests.toArray(new CustomUrlRequest[0]); + } + else if(!Strings.isNullOrEmpty(csvFilePath)){ + CustomUrlManager customUrlManager = new CustomUrlManager(PreferenceHelper.getInstance()); + List csvCustomUrlRequests = customUrlManager.getCustomUrlRequestsFromCSV(new File(csvFilePath)); + urlRequests = csvCustomUrlRequests.toArray(new CustomUrlRequest[0]); + } + else if(serializedRequests != null && serializedRequests.length > 0) { + urlRequests = new CustomUrlRequest[serializedRequests.length]; + for (int i = 0; i < serializedRequests.length; i++) { + urlRequests[i] = Strings.deserializeFromJson(serializedRequests[i], CustomUrlRequest.class); + } + } + return urlRequests; + } + + @NonNull + private UploadEvents.BaseUploadEvent getCallbackEvent() { + String callbackType = getInputData().getString("callbackType"); + UploadEvents.BaseUploadEvent callbackEvent = new UploadEvents.CustomUrl(); + + if(!Strings.isNullOrEmpty(callbackType) && callbackType.equals("opengts")){ + callbackEvent = new UploadEvents.OpenGTS(); + } + return callbackEvent; + } + protected int getRetryLimit() { return 3; } diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java index df62aad1a..23bb453bd 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java @@ -29,6 +29,7 @@ import java.io.Reader; import java.util.ArrayList; import java.util.Date; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -51,12 +52,13 @@ public void uploadFile(List files) { foundFileToSend = true; String tag = String.valueOf(Objects.hashCode(f.getName())); - Data data = new Data.Builder() - .putString("csvFilePath", f.getAbsolutePath()) - .putString("callbackType", "customUrl") - .build(); - OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(CustomUrlWorker.class, data); + HashMap dataMap = new HashMap() {{ + put("csvFilePath", f.getAbsolutePath()); + put("callbackType", "customUrl"); + }}; + + OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(CustomUrlWorker.class, dataMap); WorkManager.getInstance(AppSettings.getInstance()) .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); @@ -162,18 +164,16 @@ private String unApplyDecimalComma(String recordValue) { public void sendByHttp(String url, String method, String body, String headers, String username, String password){ - CustomUrlRequest request = new CustomUrlRequest(url, method, - body, headers, username, password); - + CustomUrlRequest request = new CustomUrlRequest(url, method, body, headers, username, password); String serializedRequest = Strings.serializeTojson(request); String tag = String.valueOf(Objects.hashCode(serializedRequest)); - Data data = new Data.Builder() - .putStringArray("urlRequests", new String[]{serializedRequest}) - .putString("callbackType", "customUrl") - .build(); + HashMap dataMap = new HashMap() {{ + put("urlRequests", new String[]{serializedRequest}); + put("callbackType", "customUrl"); + }}; - OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(CustomUrlWorker.class, data); + OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(CustomUrlWorker.class, dataMap); WorkManager.getInstance(AppSettings.getInstance()) .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); } diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java index 9d4582930..face18186 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java @@ -64,12 +64,13 @@ public void uploadFile(List files) { for (File f : files) { if (f.getName().endsWith(".gpx")) { String tag = String.valueOf(Objects.hashCode(f.getName())); - Data data = new Data.Builder() - .putString("gpxFilePath", f.getAbsolutePath()) - .putString("callbackType", "opengts") - .build(); - OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(CustomUrlWorker.class, data); + HashMap dataMap = new HashMap(){{ + put("gpxFilePath", f.getAbsolutePath()); + put("callbackType", "opengts"); + }}; + + OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(CustomUrlWorker.class, dataMap); WorkManager.getInstance(AppSettings.getInstance()) .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); @@ -107,23 +108,20 @@ void sendByHttp(String deviceId, String accountName, SerializableLocation[] loca serializedRequests[i] = Strings.serializeTojson(request); } - String tag = String.valueOf(Objects.hashCode(serializedRequests)); - Data data = new Data.Builder() - .putStringArray("urlRequests", serializedRequests) - .putString("callbackType", "opengts") - .build(); - OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(CustomUrlWorker.class, data); + HashMap dataMap = new HashMap(){{ + put("urlRequests", serializedRequests); + put("callbackType", "opengts"); + }}; + + OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(CustomUrlWorker.class, dataMap); WorkManager.getInstance(AppSettings.getInstance()) .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); - } - - /** * Encode a location as GPRMC string data. *

From d41c1efe0b5f1bc169b554f695901cd70eef21b7 Mon Sep 17 00:00:00 2001 From: mendhak Date: Sat, 16 Mar 2024 21:07:05 +0000 Subject: [PATCH 16/73] Pass input data as a hashmap in the standard worker builder --- .../src/main/java/com/mendhak/gpslogger/common/Systems.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java b/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java index 15c29846c..d09a70e65 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java @@ -251,13 +251,12 @@ else if(appThemeSetting.equalsIgnoreCase("light")){ */ public static OneTimeWorkRequest getBasicOneTimeWorkRequest(Class workerClass, HashMap dataMap) { - androidx.work.Data.Builder dataBuilder = new Data.Builder(); - dataBuilder.putAll(dataMap); - Data data = dataBuilder.build(); + androidx.work.Data data = new Data.Builder().putAll(dataMap).build(); Constraints constraints = new Constraints.Builder() .setRequiredNetworkType(PreferenceHelper.getInstance().shouldAutoSendOnWifiOnly() ? NetworkType.UNMETERED: NetworkType.CONNECTED) .build(); + OneTimeWorkRequest workRequest = new OneTimeWorkRequest .Builder(workerClass) .setConstraints(constraints) From a14cc03df621b127337b8d4aa58c9a2d7f86ae7e Mon Sep 17 00:00:00 2001 From: mendhak Date: Sat, 16 Mar 2024 21:14:41 +0000 Subject: [PATCH 17/73] Remove unused imports --- .../mendhak/gpslogger/senders/customurl/CustomUrlManager.java | 4 ---- .../com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java | 4 ---- 2 files changed, 8 deletions(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java index 23bb453bd..916b86ffd 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java @@ -3,11 +3,7 @@ import android.location.Location; import android.os.Bundle; -import androidx.work.BackoffPolicy; -import androidx.work.Constraints; -import androidx.work.Data; import androidx.work.ExistingWorkPolicy; -import androidx.work.NetworkType; import androidx.work.OneTimeWorkRequest; import androidx.work.WorkManager; import com.mendhak.gpslogger.common.AppSettings; diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java index face18186..69d972a20 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java @@ -19,11 +19,7 @@ package com.mendhak.gpslogger.senders.opengts; -import androidx.work.BackoffPolicy; -import androidx.work.Constraints; -import androidx.work.Data; import androidx.work.ExistingWorkPolicy; -import androidx.work.NetworkType; import androidx.work.OneTimeWorkRequest; import androidx.work.WorkManager; From b103a63210935ad060f77274f51b33006c34a081 Mon Sep 17 00:00:00 2001 From: mendhak Date: Sat, 16 Mar 2024 21:16:56 +0000 Subject: [PATCH 18/73] Custom URL Job no longer required --- .../loggers/customurl/CustomUrlJob.java | 140 ------------------ 1 file changed, 140 deletions(-) delete mode 100644 gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlJob.java diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlJob.java b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlJob.java deleted file mode 100644 index 2a76ff198..000000000 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlJob.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2016 mendhak - * - * This file is part of GPSLogger for Android. - * - * GPSLogger for Android is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * GPSLogger for Android is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GPSLogger for Android. If not, see . - */ - -package com.mendhak.gpslogger.loggers.customurl; - - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.birbit.android.jobqueue.Job; -import com.birbit.android.jobqueue.Params; -import com.birbit.android.jobqueue.RetryConstraint; -import com.mendhak.gpslogger.common.AppSettings; -import com.mendhak.gpslogger.common.events.UploadEvents; -import com.mendhak.gpslogger.common.network.Networks; -import com.mendhak.gpslogger.common.slf4j.Logs; - -import org.slf4j.Logger; - -import java.io.File; -import java.util.ArrayList; -import java.util.Map; - -import javax.net.ssl.X509TrustManager; - -import de.greenrobot.event.EventBus; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; - - -public class CustomUrlJob extends Job { - - private static final Logger LOG = Logs.of(CustomUrlJob.class); - - private UploadEvents.BaseUploadEvent callbackEvent; - - private ArrayList urlRequests; - - public CustomUrlJob(ArrayList urlRequests, File csvFile, UploadEvents.BaseUploadEvent callbackEvent) { - super(new Params(1).requireNetwork().persist()); - - this.callbackEvent = callbackEvent; - this.urlRequests = urlRequests; - } - - @Override - public void onAdded() { - } - - @Override - public void onRun() throws Throwable { - - boolean success = true; - String responseError = null; - String responseThrowableMessage = null; - - if(urlRequests != null && urlRequests.size() > 0){ - - for (CustomUrlRequest urlRequest : urlRequests) { - LOG.info("HTTP Request - " + urlRequest.getLogURL()); - - OkHttpClient.Builder okBuilder = new OkHttpClient.Builder(); - okBuilder.sslSocketFactory(Networks.getSocketFactory(AppSettings.getInstance()), - (X509TrustManager) Networks.getTrustManager(AppSettings.getInstance())); - Request.Builder requestBuilder = new Request.Builder().url(urlRequest.getLogURL()); - - for(Map.Entry header : urlRequest.getHttpHeaders().entrySet()){ - requestBuilder.addHeader(header.getKey(), header.getValue()); - } - - if ( ! urlRequest.getHttpMethod().equalsIgnoreCase("GET")) { - RequestBody body = RequestBody.create(null, urlRequest.getHttpBody()); - requestBuilder = requestBuilder.method(urlRequest.getHttpMethod(),body); - } - - Request request = requestBuilder.build(); - Response response = okBuilder.build().newCall(request).execute(); - - if (response.isSuccessful()) { - LOG.debug("HTTP request complete with successful response code " + response); - } - else { - LOG.error("HTTP request complete with unexpected response code " + response ); - responseError = "Unexpected code " + response; - responseThrowableMessage = response.body().string(); - success = false; - } - - response.body().close(); - - if(!success){ - break; - } - } - } - - if(success){ - EventBus.getDefault().post(callbackEvent.succeeded()); - } - else { - EventBus.getDefault().post(callbackEvent.failed("Unexpected code " + responseError, new Throwable(responseThrowableMessage))); - } - } - - @Override - protected void onCancel(int cancelReason, @Nullable Throwable throwable) { - EventBus.getDefault().post(callbackEvent.failed("Could not send to custom URL", throwable)); - LOG.error("Custom URL: maximum attempts failed, giving up", throwable); - } - - @Override - protected RetryConstraint shouldReRunOnThrowable(@NonNull Throwable throwable, int runCount, int maxRunCount) { - LOG.warn(String.format("Custom URL: attempt %d failed, maximum %d attempts", runCount, maxRunCount)); - return RetryConstraint.createExponentialBackoff(runCount, 5000); - } - - - @Override - protected int getRetryLimit() { - return 3; - } -} From 4a7ecb3187befa71abce7ab3bbee06b85ed247d1 Mon Sep 17 00:00:00 2001 From: mendhak Date: Sat, 16 Mar 2024 23:00:15 +0000 Subject: [PATCH 19/73] UDP implementation for OpenGTS Background Worker --- .../loggers/opengts/OpenGtsUdpWorker.java | 82 +++++++++++++++++++ .../senders/opengts/OpenGTSManager.java | 47 ++++++++--- 2 files changed, 116 insertions(+), 13 deletions(-) create mode 100644 gpslogger/src/main/java/com/mendhak/gpslogger/loggers/opengts/OpenGtsUdpWorker.java diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/opengts/OpenGtsUdpWorker.java b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/opengts/OpenGtsUdpWorker.java new file mode 100644 index 000000000..9debbc0be --- /dev/null +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/opengts/OpenGtsUdpWorker.java @@ -0,0 +1,82 @@ +package com.mendhak.gpslogger.loggers.opengts; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.work.Worker; +import androidx.work.WorkerParameters; + +import com.mendhak.gpslogger.common.PreferenceHelper; +import com.mendhak.gpslogger.common.SerializableLocation; +import com.mendhak.gpslogger.common.Strings; +import com.mendhak.gpslogger.common.events.UploadEvents; +import com.mendhak.gpslogger.common.slf4j.Logs; +import com.mendhak.gpslogger.senders.GpxReader; +import com.mendhak.gpslogger.senders.opengts.OpenGTSManager; + +import org.slf4j.Logger; + +import java.io.File; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.util.List; + +import de.greenrobot.event.EventBus; + +public class OpenGtsUdpWorker extends Worker{ + + public static final Logger LOG = Logs.of(OpenGtsUdpWorker.class); + public OpenGtsUdpWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { + super(context, workerParams); + } + @NonNull + @Override + public Result doWork() { + + PreferenceHelper preferenceHelper = PreferenceHelper.getInstance(); + String server = preferenceHelper.getOpenGTSServer(); + int port = Integer.valueOf(preferenceHelper.getOpenGTSServerPort()); + String accountName = preferenceHelper.getOpenGTSAccountName(); + String deviceId = preferenceHelper.getOpenGTSDeviceId(); + + String gpxFilePath = getInputData().getString("gpxFilePath"); + String[] serializedLocations = getInputData().getStringArray("locations"); + + try { + if(!Strings.isNullOrEmpty(gpxFilePath)){ + List locations = GpxReader.getPoints(new File(gpxFilePath)); + sendRAW(deviceId, accountName, server, port, locations.toArray(new SerializableLocation[0])); + } + else if (serializedLocations != null && serializedLocations.length > 0){ + SerializableLocation[] locations = new SerializableLocation[serializedLocations.length]; + for (int i = 0; i < serializedLocations.length; i++) { + locations[i] = Strings.deserializeFromJson(serializedLocations[i], SerializableLocation.class); + } + sendRAW(deviceId, accountName, server, port, locations); + } + EventBus.getDefault().post(new UploadEvents.OpenGTS().succeeded()); + } + catch(Exception ex){ + LOG.error("Could not send to OpenGTS", ex); + EventBus.getDefault().post(new UploadEvents.OpenGTS().failed("Could not send to OpenGTS", ex)); + return Result.failure(); + } + return Result.success(); + } + + private void sendRAW(String id, String accountName, String server, int port, SerializableLocation[] locations) throws Exception { + for (SerializableLocation loc : locations) { + if(Strings.isNullOrEmpty(accountName)){ + accountName = id; + } + String message = accountName + "/" + id + "/" + OpenGTSManager.gprmcEncode(loc); + DatagramSocket socket = new DatagramSocket(); + byte[] buffer = message.getBytes(); + DatagramPacket packet = new DatagramPacket(buffer, buffer.length, InetAddress.getByName(server), port); + LOG.debug("Sending UDP " + message); + socket.send(packet); + socket.close(); + } + } +} diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java index 69d972a20..ca24b374e 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java @@ -23,12 +23,11 @@ import androidx.work.OneTimeWorkRequest; import androidx.work.WorkManager; -import com.birbit.android.jobqueue.JobManager; import com.mendhak.gpslogger.common.*; import com.mendhak.gpslogger.common.slf4j.Logs; import com.mendhak.gpslogger.loggers.customurl.CustomUrlRequest; import com.mendhak.gpslogger.loggers.customurl.CustomUrlWorker; -import com.mendhak.gpslogger.loggers.opengts.OpenGtsUdpJob; +import com.mendhak.gpslogger.loggers.opengts.OpenGtsUdpWorker; import com.mendhak.gpslogger.senders.FileSender; import com.mendhak.gpslogger.senders.GpxReader; import org.slf4j.Logger; @@ -59,16 +58,28 @@ public void uploadFile(List files) { // Use only gpx for (File f : files) { if (f.getName().endsWith(".gpx")) { - String tag = String.valueOf(Objects.hashCode(f.getName())); - HashMap dataMap = new HashMap(){{ - put("gpxFilePath", f.getAbsolutePath()); - put("callbackType", "opengts"); - }}; - - OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(CustomUrlWorker.class, dataMap); - WorkManager.getInstance(AppSettings.getInstance()) - .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); + String communication = preferenceHelper.getOpenGTSServerCommunicationMethod(); + + if(communication.equalsIgnoreCase("udp")){ + String tag = String.valueOf(Objects.hashCode(f.getName())); + HashMap dataMap = new HashMap(){{ + put("gpxFilePath", f.getAbsolutePath()); + }}; + OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(OpenGtsUdpWorker.class, dataMap); + WorkManager.getInstance(AppSettings.getInstance()) + .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); + } + else { + String tag = String.valueOf(Objects.hashCode(f.getName())); + HashMap dataMap = new HashMap(){{ + put("gpxFilePath", f.getAbsolutePath()); + put("callbackType", "opengts"); + }}; + OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(CustomUrlWorker.class, dataMap); + WorkManager.getInstance(AppSettings.getInstance()) + .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); + } } } @@ -85,8 +96,18 @@ public void sendLocations(SerializableLocation[] locations){ String communication = preferenceHelper.getOpenGTSServerCommunicationMethod(); if(communication.equalsIgnoreCase("udp")){ - JobManager jobManager = AppSettings.getJobManager(); - jobManager.addJobInBackground(new OpenGtsUdpJob(server, port, accountName, path, deviceId, communication, locations)); + String tag = String.valueOf(Objects.hashCode(locations)); + String[] serializedLocations = new String[locations.length]; + for (int i = 0; i < locations.length; i++) { + serializedLocations[i] = Strings.serializeTojson(locations[i]); + } + HashMap dataMap = new HashMap(){{ + put("locations", serializedLocations); + }}; + + OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(OpenGtsUdpWorker.class, dataMap); + WorkManager.getInstance(AppSettings.getInstance()) + .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); } else { sendByHttp(deviceId, accountName, locations, communication, path, server, port); From 62a613d1ffbf66f4bff14d5b21065a8883b2c8e4 Mon Sep 17 00:00:00 2001 From: mendhak Date: Sun, 17 Mar 2024 09:03:01 +0000 Subject: [PATCH 20/73] Remove unused OpenGTS UDP Job class --- .../loggers/opengts/OpenGtsUdpJob.java | 107 ------------------ 1 file changed, 107 deletions(-) delete mode 100644 gpslogger/src/main/java/com/mendhak/gpslogger/loggers/opengts/OpenGtsUdpJob.java diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/opengts/OpenGtsUdpJob.java b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/opengts/OpenGtsUdpJob.java deleted file mode 100644 index 60a08378b..000000000 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/opengts/OpenGtsUdpJob.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2016 mendhak - * - * This file is part of GPSLogger for Android. - * - * GPSLogger for Android is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * GPSLogger for Android is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GPSLogger for Android. If not, see . - */ - -package com.mendhak.gpslogger.loggers.opengts; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.birbit.android.jobqueue.Job; -import com.birbit.android.jobqueue.Params; -import com.birbit.android.jobqueue.RetryConstraint; -import com.mendhak.gpslogger.common.SerializableLocation; -import com.mendhak.gpslogger.common.Strings; -import com.mendhak.gpslogger.common.events.UploadEvents; -import com.mendhak.gpslogger.common.slf4j.Logs; -import com.mendhak.gpslogger.senders.opengts.OpenGTSManager; -import de.greenrobot.event.EventBus; -import org.slf4j.Logger; - -import java.net.*; - - -public class OpenGtsUdpJob extends Job { - - String server; - int port ; - String accountName ; - String path ; - String deviceId ; - String communication; - SerializableLocation[] locations; - private static final Logger LOG = Logs.of(OpenGtsUdpJob.class); - - public OpenGtsUdpJob(String server, int port, String accountName, String path, String deviceId, String communication, SerializableLocation[] locations){ - super(new Params(1).requireNetwork().persist()); - - this.server = server; - this.port = port; - this.accountName = accountName; - this.path = path; - this.deviceId = deviceId; - this.communication = communication; - this.locations = locations; - } - - @Override - public void onAdded() { - - } - - @Override - public void onRun() throws Throwable { - - LOG.debug("Running OpenGTS Job"); - sendRAW(deviceId, accountName, locations); - EventBus.getDefault().post(new UploadEvents.OpenGTS().succeeded()); - } - - @Override - protected void onCancel(int cancelReason, @Nullable Throwable throwable) { - - } - - @Override - protected RetryConstraint shouldReRunOnThrowable(@NonNull Throwable throwable, int runCount, int maxRunCount) { - LOG.error("Could not send to OpenGTS", throwable); - EventBus.getDefault().post(new UploadEvents.OpenGTS().failed("Could not send to OpenGTS", throwable)); - return RetryConstraint.CANCEL; - } - - - public void sendRAW(String id, String accountName, SerializableLocation[] locations) throws Exception { - for (SerializableLocation loc : locations) { - if(Strings.isNullOrEmpty(accountName)){ - accountName = id; - } - String message = accountName + "/" + id + "/" + OpenGTSManager.gprmcEncode(loc); - DatagramSocket socket = new DatagramSocket(); - byte[] buffer = message.getBytes(); - DatagramPacket packet = new DatagramPacket(buffer, buffer.length, InetAddress.getByName(server), port); - LOG.debug("Sending UDP " + message); - socket.send(packet); - socket.close(); - } - } - - - - - -} From d502377890b5fb84d693d7924efa2b413b8362a9 Mon Sep 17 00:00:00 2001 From: mendhak Date: Sun, 17 Mar 2024 10:15:31 +0000 Subject: [PATCH 21/73] Dropbox Worker using WorkManager instead of Jobqueue --- .../senders/dropbox/DropBoxManager.java | 21 +++--- .../senders/dropbox/DropboxWorker.java | 75 +++++++++++++++++++ 2 files changed, 86 insertions(+), 10 deletions(-) create mode 100644 gpslogger/src/main/java/com/mendhak/gpslogger/senders/dropbox/DropboxWorker.java diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/dropbox/DropBoxManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/dropbox/DropBoxManager.java index 8e1c4910d..89f90affb 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/dropbox/DropBoxManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/dropbox/DropBoxManager.java @@ -22,20 +22,22 @@ import android.content.Context; -import com.birbit.android.jobqueue.CancelResult; -import com.birbit.android.jobqueue.JobManager; -import com.birbit.android.jobqueue.TagConstraint; +import androidx.work.OneTimeWorkRequest; +import androidx.work.WorkManager; + import com.dropbox.core.*; import com.dropbox.core.android.Auth; import com.dropbox.core.oauth.DbxCredential; import com.mendhak.gpslogger.common.AppSettings; import com.mendhak.gpslogger.common.PreferenceHelper; import com.mendhak.gpslogger.common.Strings; +import com.mendhak.gpslogger.common.Systems; import com.mendhak.gpslogger.common.slf4j.Logs; import com.mendhak.gpslogger.senders.FileSender; import org.slf4j.Logger; import java.io.File; +import java.util.HashMap; import java.util.List; @@ -106,13 +108,12 @@ public String getName() { public void uploadFile(final String fileName) { - final JobManager jobManager = AppSettings.getJobManager(); - jobManager.cancelJobsInBackground(new CancelResult.AsyncCancelCallback() { - @Override - public void onCancelled(CancelResult cancelResult) { - jobManager.addJobInBackground(new DropboxJob(fileName)); - } - }, TagConstraint.ANY, DropboxJob.getJobTag(fileName)); + HashMap dataMap = new HashMap(){{ + put("fileName", fileName); + }}; + + OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(DropboxWorker.class, dataMap); + WorkManager.getInstance(AppSettings.getInstance()).enqueue(workRequest); } @Override diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/dropbox/DropboxWorker.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/dropbox/DropboxWorker.java new file mode 100644 index 000000000..f8dc1a4a2 --- /dev/null +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/dropbox/DropboxWorker.java @@ -0,0 +1,75 @@ +package com.mendhak.gpslogger.senders.dropbox; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.work.Worker; +import androidx.work.WorkerParameters; + +import com.dropbox.core.DbxRequestConfig; +import com.dropbox.core.oauth.DbxCredential; +import com.dropbox.core.v2.DbxClientV2; +import com.dropbox.core.v2.files.WriteMode; +import com.mendhak.gpslogger.common.PreferenceHelper; +import com.mendhak.gpslogger.common.Strings; +import com.mendhak.gpslogger.common.events.UploadEvents; +import com.mendhak.gpslogger.common.slf4j.Logs; + +import org.slf4j.Logger; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; + +import de.greenrobot.event.EventBus; + +public class DropboxWorker extends Worker { + + private static final Logger LOG = Logs.of(DropboxWorker.class); + public DropboxWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { + super(context, workerParams); + } + + @NonNull + @Override + public Result doWork() { + + String fileName = getInputData().getString("fileName"); + if(Strings.isNullOrEmpty(fileName)) { + EventBus.getDefault().post(new UploadEvents.Dropbox().failed("Dropbox upload failed", new Throwable("Nothing to upload."))); + return Result.failure(); + } + + File gpsDir = new File(PreferenceHelper.getInstance().getGpsLoggerFolder()); + File gpxFile = new File(gpsDir, fileName); + + try { + LOG.debug("Beginning upload to dropbox..."); + InputStream inputStream = new FileInputStream(gpxFile); + DbxRequestConfig requestConfig = DbxRequestConfig.newBuilder("GPSLogger").build(); + DbxClientV2 mDbxClient; + + if(!Strings.isNullOrEmpty(PreferenceHelper.getInstance().getDropboxRefreshToken())){ + DbxCredential dropboxCred = DbxCredential.Reader.readFully(PreferenceHelper.getInstance().getDropboxRefreshToken()); + mDbxClient = new DbxClientV2(requestConfig, dropboxCred); + } + else { + //For existing users that already have long lived access tokens stored. + mDbxClient = new DbxClientV2(requestConfig, PreferenceHelper.getInstance().getDropboxLongLivedAccessKey()); + } + + mDbxClient.files().uploadBuilder("/" + fileName).withMode(WriteMode.OVERWRITE).uploadAndFinish(inputStream); + + EventBus.getDefault().post(new UploadEvents.Dropbox().succeeded()); + LOG.info("Dropbox - file uploaded"); + } catch (Exception e) { + LOG.error("Could not upload to Dropbox" , e); + EventBus.getDefault().post(new UploadEvents.Dropbox().failed(e.getMessage(), e)); + return Result.failure(); + } + + + + return Result.success(); + } +} From ccc584403eecf4d97e8dbb6301a5fdfb4fd64d71 Mon Sep 17 00:00:00 2001 From: mendhak Date: Sun, 17 Mar 2024 10:19:16 +0000 Subject: [PATCH 22/73] Remove unused Dropbox Job --- .../gpslogger/senders/dropbox/DropboxJob.java | 113 ------------------ 1 file changed, 113 deletions(-) delete mode 100644 gpslogger/src/main/java/com/mendhak/gpslogger/senders/dropbox/DropboxJob.java diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/dropbox/DropboxJob.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/dropbox/DropboxJob.java deleted file mode 100644 index ae003a904..000000000 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/dropbox/DropboxJob.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2016 mendhak - * - * This file is part of GPSLogger for Android. - * - * GPSLogger for Android is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * GPSLogger for Android is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GPSLogger for Android. If not, see . - */ - -package com.mendhak.gpslogger.senders.dropbox; - - - - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.birbit.android.jobqueue.Job; -import com.birbit.android.jobqueue.Params; -import com.birbit.android.jobqueue.RetryConstraint; -import com.dropbox.core.DbxRequestConfig; -import com.dropbox.core.oauth.DbxCredential; -import com.dropbox.core.v2.DbxClientV2; -import com.dropbox.core.v2.files.WriteMode; -import com.mendhak.gpslogger.common.PreferenceHelper; -import com.mendhak.gpslogger.common.Strings; -import com.mendhak.gpslogger.common.events.UploadEvents; -import com.mendhak.gpslogger.common.slf4j.Logs; -import de.greenrobot.event.EventBus; -import org.slf4j.Logger; - -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStream; - - -public class DropboxJob extends Job { - - - private static final Logger LOG = Logs.of(DropboxJob.class); - private static PreferenceHelper preferenceHelper = PreferenceHelper.getInstance(); - String fileName; - - - protected DropboxJob(String fileName) { - super(new Params(1).requireNetwork().persist().addTags(getJobTag(fileName))); - - this.fileName = fileName; - } - - @Override - public void onAdded() { - LOG.debug("Dropbox job added"); - } - - @Override - public void onRun() throws Throwable { - File gpsDir = new File(preferenceHelper.getGpsLoggerFolder()); - File gpxFile = new File(gpsDir, fileName); - - try { - LOG.debug("Beginning upload to dropbox..."); - InputStream inputStream = new FileInputStream(gpxFile); - DbxRequestConfig requestConfig = DbxRequestConfig.newBuilder("GPSLogger").build(); - DbxClientV2 mDbxClient; - - if(!Strings.isNullOrEmpty(PreferenceHelper.getInstance().getDropboxRefreshToken())){ - DbxCredential dropboxCred = DbxCredential.Reader.readFully(PreferenceHelper.getInstance().getDropboxRefreshToken()); - mDbxClient = new DbxClientV2(requestConfig, dropboxCred); - } - else { - //For existing users that already have long lived access tokens stored. - mDbxClient = new DbxClientV2(requestConfig, PreferenceHelper.getInstance().getDropboxLongLivedAccessKey()); - } - - mDbxClient.files().uploadBuilder("/" + fileName).withMode(WriteMode.OVERWRITE).uploadAndFinish(inputStream); - - EventBus.getDefault().post(new UploadEvents.Dropbox().succeeded()); - LOG.info("Dropbox - file uploaded"); - } catch (Exception e) { - LOG.error("Could not upload to Dropbox" , e); - EventBus.getDefault().post(new UploadEvents.Dropbox().failed(e.getMessage(), e)); - } - - } - - @Override - protected void onCancel(int cancelReason, @Nullable Throwable throwable) { - - } - - @Override - protected RetryConstraint shouldReRunOnThrowable(@NonNull Throwable throwable, int runCount, int maxRunCount) { - EventBus.getDefault().post(new UploadEvents.Dropbox().failed("Could not upload to Dropbox", throwable)); - LOG.error("Could not upload to Dropbox", throwable); - return RetryConstraint.CANCEL; - } - - - public static String getJobTag(String fileName) { - return "DROPBOX" + fileName; - } -} From ee1092103e50b60d32a6366eadd6d91a5962a9e0 Mon Sep 17 00:00:00 2001 From: mendhak Date: Sun, 17 Mar 2024 11:29:24 +0000 Subject: [PATCH 23/73] Replace Auto email job with Work manager --- .../senders/email/AutoEmailManager.java | 57 +++-- .../senders/email/AutoEmailWorker.java | 234 ++++++++++++++++++ .../fragments/settings/AutoEmailFragment.java | 4 +- 3 files changed, 265 insertions(+), 30 deletions(-) create mode 100644 gpslogger/src/main/java/com/mendhak/gpslogger/senders/email/AutoEmailWorker.java diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/email/AutoEmailManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/email/AutoEmailManager.java index d921418cb..2b91b96de 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/email/AutoEmailManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/email/AutoEmailManager.java @@ -18,18 +18,22 @@ */ package com.mendhak.gpslogger.senders.email; -import com.birbit.android.jobqueue.CancelResult; -import com.birbit.android.jobqueue.JobManager; -import com.birbit.android.jobqueue.TagConstraint; +import androidx.work.ExistingWorkPolicy; +import androidx.work.OneTimeWorkRequest; +import androidx.work.WorkManager; + + import com.mendhak.gpslogger.common.AppSettings; import com.mendhak.gpslogger.common.PreferenceHelper; import com.mendhak.gpslogger.common.Strings; +import com.mendhak.gpslogger.common.Systems; import com.mendhak.gpslogger.senders.FileSender; import java.io.File; -import java.util.ArrayList; import java.util.Date; +import java.util.HashMap; import java.util.List; +import java.util.Objects; public class AutoEmailManager extends FileSender { @@ -42,28 +46,24 @@ public AutoEmailManager(PreferenceHelper helper) { @Override public void uploadFile(List files) { - final ArrayList filesToSend = new ArrayList<>(); - - //If a zip file exists, remove others - for (File f : files) { - filesToSend.add(f); + String[] fileNames = new String[files.size()]; + for (int i = 0; i < files.size(); i++) { + fileNames[i] = files.get(i).getAbsolutePath(); } final String subject = "GPS Log file generated at "+ Strings.getReadableDateTime(new Date()); - final String body = "GPS Log file generated at "+ Strings.getReadableDateTime(new Date()); - final JobManager jobManager = AppSettings.getJobManager(); - jobManager.cancelJobsInBackground(new CancelResult.AsyncCancelCallback() { - @Override - public void onCancelled(CancelResult cancelResult) { - jobManager.addJobInBackground(new AutoEmailJob(preferenceHelper.getSmtpServer(), - preferenceHelper.getSmtpPort(), preferenceHelper.getSmtpUsername(), preferenceHelper.getSmtpPassword(), - preferenceHelper.isSmtpSsl(), preferenceHelper.getAutoEmailTargets(), preferenceHelper.getSmtpSenderAddress(), - subject, body, filesToSend.toArray(new File[filesToSend.size()]))); - } - }, TagConstraint.ANY, AutoEmailJob.getJobTag(filesToSend.toArray(new File[filesToSend.size()]))); + HashMap dataMap = new HashMap() {{ + put("subject", subject); + put("body", body); + put("fileNames", fileNames); + }}; + String tag = String.valueOf(Objects.hashCode(fileNames)) ; + OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(AutoEmailWorker.class, dataMap); + WorkManager.getInstance(AppSettings.getInstance()) + .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); } @@ -83,18 +83,21 @@ public String getName() { } - public void sendTestEmail(String smtpServer, String smtpPort, - String smtpUsername, String smtpPassword, boolean smtpUseSsl, - String emailTarget, String fromAddress) { + public void sendTestEmail() { String subject = "Test Email from GPSLogger at " + Strings.getReadableDateTime(new Date()); String body ="Test Email from GPSLogger at " + Strings.getReadableDateTime(new Date()); - JobManager jobManager = AppSettings.getJobManager(); - jobManager.addJobInBackground(new AutoEmailJob(smtpServer, - smtpPort, smtpUsername, smtpPassword, smtpUseSsl, - emailTarget, fromAddress, subject, body, new File[]{})); + HashMap dataMap = new HashMap() {{ + put("subject", subject); + put("body", body); + put("fileNames", new String[]{}); + }}; + String tag = String.valueOf(Objects.hashCode(new String[]{})) ; + OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(AutoEmailWorker.class, dataMap); + WorkManager.getInstance(AppSettings.getInstance()) + .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); } @Override diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/email/AutoEmailWorker.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/email/AutoEmailWorker.java new file mode 100644 index 000000000..9e4a88bf9 --- /dev/null +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/email/AutoEmailWorker.java @@ -0,0 +1,234 @@ +package com.mendhak.gpslogger.senders.email; + +import android.content.Context; +import android.util.Base64; + +import androidx.annotation.NonNull; +import androidx.work.Worker; +import androidx.work.WorkerParameters; + +import com.mendhak.gpslogger.common.AppSettings; +import com.mendhak.gpslogger.common.PreferenceHelper; +import com.mendhak.gpslogger.common.Strings; +import com.mendhak.gpslogger.common.events.UploadEvents; +import com.mendhak.gpslogger.common.network.LocalX509TrustManager; +import com.mendhak.gpslogger.common.network.Networks; +import com.mendhak.gpslogger.common.slf4j.Logs; +import com.mendhak.gpslogger.loggers.Files; +import com.mendhak.gpslogger.loggers.Streams; + +import org.apache.commons.net.ProtocolCommandEvent; +import org.apache.commons.net.ProtocolCommandListener; +import org.apache.commons.net.smtp.AuthenticatingSMTPClient; +import org.apache.commons.net.smtp.SMTPClient; +import org.apache.commons.net.smtp.SMTPReply; +import org.apache.commons.net.smtp.SimpleSMTPHeader; +import org.slf4j.Logger; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.UUID; + +import de.greenrobot.event.EventBus; + +public class AutoEmailWorker extends Worker { + + private static final Logger LOG = Logs.of(AutoEmailWorker.class); + public AutoEmailWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { + super(context, workerParams); + } + + @NonNull + @Override + public Result doWork() { + + PreferenceHelper preferenceHelper = PreferenceHelper.getInstance(); + String smtpServer = preferenceHelper.getSmtpServer(); + String smtpPort = preferenceHelper.getSmtpPort(); + String smtpPassword = preferenceHelper.getSmtpPassword(); + String smtpUsername = preferenceHelper.getSmtpUsername(); + boolean smtpUseSsl = preferenceHelper.isSmtpSsl(); + String csvEmailTargets = preferenceHelper.getAutoEmailTargets(); + String fromAddress = preferenceHelper.getSmtpSenderAddress(); + + String subject = getInputData().getString("subject"); + String body = getInputData().getString("body"); + String[] fileNames = getInputData().getStringArray("fileNames"); + + File[] files = new File[fileNames.length]; + for (int i = 0; i < fileNames.length; i++) { + files[i] = new File(fileNames[i]); + } + + int port = Strings.toInt(smtpPort,25); + + if (Strings.isNullOrEmpty(fromAddress)) { + fromAddress = smtpUsername; + } + + AuthenticatingSMTPClient client = new AuthenticatingSMTPClient(); + + ArrayList smtpServerResponses = new ArrayList<>(); + UploadEvents.AutoEmail smtpFailureEvent = null; + + try { + + client.addProtocolCommandListener(new ProtocolCommandListener() { + @Override + public void protocolCommandSent(ProtocolCommandEvent event) { + LOG.debug(event.getMessage()); + smtpServerResponses.add(event.getMessage()); + } + + @Override + public void protocolReplyReceived(ProtocolCommandEvent event) { + LOG.debug(event.getMessage()); + smtpServerResponses.add(event.getMessage()); + } + }); + + if(smtpUseSsl){ + client = new AuthenticatingSMTPClient("TLS",true); + } + + + // optionally set a timeout to have a faster feedback on errors + client.setDefaultTimeout(10 * 1000); + checkReply(client); + LOG.debug("Connecting to SMTP Server"); + client.connect(smtpServer, port); + checkReply(client); + // you say ehlo and you specify the host you are connecting from, could be anything + client.ehlo("localhost"); + checkReply(client); + // if your host accepts STARTTLS, we're good everything will be encrypted, otherwise we're done here + LOG.debug("Checking TLS..."); + + client.setTrustManager(new LocalX509TrustManager(Networks.getKnownServersStore(AppSettings.getInstance()))); + if(!smtpUseSsl && client.execTLS()){ + client.ehlo("localhost"); + } + + client.auth(AuthenticatingSMTPClient.AUTH_METHOD.LOGIN, smtpUsername, smtpPassword); + checkReply(client); + + client.setSender(fromAddress); + checkReply(client); + + String target = csvEmailTargets.split(",")[0]; + client.addRecipient(target); + + SimpleSMTPHeader header = new SimpleSMTPHeader(fromAddress, target, subject); + + //Multiple email targets? + for (String ccTarget : csvEmailTargets.split(",")) { + if (!ccTarget.equalsIgnoreCase(target)) { + header.addCC(ccTarget); + client.addRecipient(ccTarget); + } + } + + checkReply(client); + + Writer writer = client.sendMessageData(); + + if (writer != null) { + + // Regular email with just a body + if (files == null || files.length == 0) { + header.addHeaderField("Content-Type", "text/plain; charset=UTF-8"); + writer.write(header.toString()); + writer.write(body); + } + // Attach files in a multipart way + else { + String boundary = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 9); + header.addHeaderField("Content-Type", "multipart/mixed; boundary=" + boundary); + writer.write(header.toString()); + + writer.write("--" + boundary + "\n"); + writer.write("Content-Type: text/plain; charset=UTF-8" + "\n\n"); + writer.write(body); + writer.write("\n"); + + attachFilesToWriter(writer, boundary, files); + writer.write("--" + boundary + "--\n\n"); + } + + + writer.close(); + if (!client.completePendingCommand()) {// failure + smtpFailureEvent = new UploadEvents.AutoEmail().failed("Failure to send the email"); + } + else { + LOG.info("Email - file sent"); + EventBus.getDefault().post(new UploadEvents.AutoEmail().succeeded()); + } + } + else { + smtpFailureEvent = new UploadEvents.AutoEmail().failed("Failure to send the email"); + } + + } + catch (Exception e) { + LOG.error("Could not send email ", e); + smtpFailureEvent = new UploadEvents.AutoEmail().failed("Could not send email " + e.getMessage() , e); + } + finally { + try{ + client.logout(); + client.disconnect(); + } + catch (Exception ignored) { + } + + if(smtpFailureEvent != null){ + smtpFailureEvent.smtpMessages = smtpServerResponses; + if(smtpFailureEvent.smtpMessages.isEmpty()){ + smtpFailureEvent.smtpMessages = new ArrayList<>(Arrays.asList(client.getReplyStrings())); + } + EventBus.getDefault().post(smtpFailureEvent); + return Result.failure(); + } + } + + + return Result.success(); + } + + + /** + * Append the given attachments to the message which is being written by the given writer. + * + * @param boundary separates each file attachment + */ + private static void attachFilesToWriter(Writer writer, String boundary, File[] files) throws IOException { + for (File f : files) { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream((int) f.length()); + FileInputStream inputStream = new FileInputStream(f); + Streams.copyIntoStream(inputStream, outputStream); + + writer.write("--" + boundary + "\n"); + writer.write("Content-Type: application/" + Files.getMimeTypeFromFileName(f.getName()) + "; name=\"" + f.getName() + "\"\n"); + writer.write("Content-Disposition: attachment; filename=\"" + f.getName() + "\"\n"); + writer.write("Content-Transfer-Encoding: base64\n\n"); + String encodedFile = Base64.encodeToString(outputStream.toByteArray(), Base64.DEFAULT); + writer.write(encodedFile); + writer.write("\n"); + } + } + + + private static void checkReply(SMTPClient sc) throws Exception { + if (SMTPReply.isNegativeTransient(sc.getReplyCode())) { + throw new Exception("Transient SMTP error " + sc.getReplyString()); + } else if (SMTPReply.isNegativePermanent(sc.getReplyCode())) { + throw new Exception("Permanent SMTP error " + sc.getReplyString()); + } + } +} diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/AutoEmailFragment.java b/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/AutoEmailFragment.java index fb393c17c..dfdb6708d 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/AutoEmailFragment.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/AutoEmailFragment.java @@ -216,9 +216,7 @@ public boolean onPreferenceClick(Preference preference) { - aem.sendTestEmail(smtpServer, smtpPort, - smtpUsername, smtpPassword, - useSsl, smtpTarget, senderAddress); + aem.sendTestEmail(); return true; } From ad7e4c7535b455db4ec73b753be423a7caa7ebb5 Mon Sep 17 00:00:00 2001 From: mendhak Date: Sun, 17 Mar 2024 11:30:14 +0000 Subject: [PATCH 24/73] Remove unused auto email job --- .../gpslogger/senders/email/AutoEmailJob.java | 276 ------------------ 1 file changed, 276 deletions(-) delete mode 100644 gpslogger/src/main/java/com/mendhak/gpslogger/senders/email/AutoEmailJob.java diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/email/AutoEmailJob.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/email/AutoEmailJob.java deleted file mode 100644 index 1ce23dcf6..000000000 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/email/AutoEmailJob.java +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright (C) 2016 mendhak - * - * This file is part of GPSLogger for Android. - * - * GPSLogger for Android is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * GPSLogger for Android is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GPSLogger for Android. If not, see . - */ - -package com.mendhak.gpslogger.senders.email; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import android.util.Base64; - -import com.birbit.android.jobqueue.Job; -import com.birbit.android.jobqueue.Params; -import com.birbit.android.jobqueue.RetryConstraint; -import com.mendhak.gpslogger.common.AppSettings; -import com.mendhak.gpslogger.common.network.Networks; -import com.mendhak.gpslogger.common.Strings; -import com.mendhak.gpslogger.common.events.UploadEvents; -import com.mendhak.gpslogger.common.slf4j.Logs; -import com.mendhak.gpslogger.loggers.Files; -import com.mendhak.gpslogger.loggers.Streams; -import com.mendhak.gpslogger.common.network.LocalX509TrustManager; -import de.greenrobot.event.EventBus; -import org.apache.commons.net.ProtocolCommandEvent; -import org.apache.commons.net.ProtocolCommandListener; -import org.apache.commons.net.smtp.*; -import org.slf4j.Logger; - -import java.io.*; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.UUID; - - -public class AutoEmailJob extends Job { - - private static final Logger LOG = Logs.of(AutoEmailJob.class); - String smtpServer; - String smtpPort; - String smtpUsername; - String smtpPassword; - boolean smtpUseSsl; - String csvEmailTargets; - String fromAddress; - String subject; - String body; - File[] files; - static ArrayList smtpServerResponses; - static UploadEvents.AutoEmail smtpFailureEvent; - - - - protected AutoEmailJob(String smtpServer, - String smtpPort, String smtpUsername, String smtpPassword, - boolean smtpUseSsl, String csvEmailTargets, String fromAddress, - String subject, String body, File[] files) { - super(new Params(1).requireNetwork().persist().addTags(getJobTag(files))); - this.smtpServer = smtpServer; - this.smtpPort = smtpPort; - this.smtpPassword = smtpPassword; - this.smtpUsername = smtpUsername; - this.smtpUseSsl = smtpUseSsl; - this.csvEmailTargets = csvEmailTargets; - this.fromAddress = fromAddress; - this.subject = subject; - this.body = body; - this.files = files; - - smtpServerResponses = new ArrayList<>(); - smtpFailureEvent = null; - } - - @Override - public void onAdded() { - - } - - @Override - public void onRun() throws Throwable { - - int port = Strings.toInt(smtpPort,25); - - if (Strings.isNullOrEmpty(fromAddress)) { - fromAddress = smtpUsername; - } - - AuthenticatingSMTPClient client = new AuthenticatingSMTPClient(); - - try { - - client.addProtocolCommandListener(new ProtocolCommandListener() { - @Override - public void protocolCommandSent(ProtocolCommandEvent event) { - LOG.debug(event.getMessage()); - smtpServerResponses.add(event.getMessage()); - } - - @Override - public void protocolReplyReceived(ProtocolCommandEvent event) { - LOG.debug(event.getMessage()); - smtpServerResponses.add(event.getMessage()); - } - }); - - if(smtpUseSsl){ - client = new AuthenticatingSMTPClient("TLS",true); - } - - - // optionally set a timeout to have a faster feedback on errors - client.setDefaultTimeout(10 * 1000); - checkReply(client); - LOG.debug("Connecting to SMTP Server"); - client.connect(smtpServer, port); - checkReply(client); - // you say ehlo and you specify the host you are connecting from, could be anything - client.ehlo("localhost"); - checkReply(client); - // if your host accepts STARTTLS, we're good everything will be encrypted, otherwise we're done here - LOG.debug("Checking TLS..."); - - client.setTrustManager(new LocalX509TrustManager(Networks.getKnownServersStore(AppSettings.getInstance()))); - if(!smtpUseSsl && client.execTLS()){ - client.ehlo("localhost"); - } - - client.auth(AuthenticatingSMTPClient.AUTH_METHOD.LOGIN, smtpUsername, smtpPassword); - checkReply(client); - - client.setSender(fromAddress); - checkReply(client); - - String target = csvEmailTargets.split(",")[0]; - client.addRecipient(target); - - SimpleSMTPHeader header = new SimpleSMTPHeader(fromAddress, target, subject); - - //Multiple email targets? - for (String ccTarget : csvEmailTargets.split(",")) { - if (!ccTarget.equalsIgnoreCase(target)) { - header.addCC(ccTarget); - client.addRecipient(ccTarget); - } - } - - checkReply(client); - - Writer writer = client.sendMessageData(); - - if (writer != null) { - - // Regular email with just a body - if (files == null || files.length == 0) { - header.addHeaderField("Content-Type", "text/plain; charset=UTF-8"); - writer.write(header.toString()); - writer.write(body); - } - // Attach files in a multipart way - else { - String boundary = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 9); - header.addHeaderField("Content-Type", "multipart/mixed; boundary=" + boundary); - writer.write(header.toString()); - - writer.write("--" + boundary + "\n"); - writer.write("Content-Type: text/plain; charset=UTF-8" + "\n\n"); - writer.write(body); - writer.write("\n"); - - attachFilesToWriter(writer, boundary, files); - writer.write("--" + boundary + "--\n\n"); - } - - - writer.close(); - if (!client.completePendingCommand()) {// failure - smtpFailureEvent = new UploadEvents.AutoEmail().failed("Failure to send the email"); - } - else { - LOG.info("Email - file sent"); - EventBus.getDefault().post(new UploadEvents.AutoEmail().succeeded()); - } - } - else { - smtpFailureEvent = new UploadEvents.AutoEmail().failed("Failure to send the email"); - } - - } - catch (Exception e) { - LOG.error("Could not send email ", e); - smtpFailureEvent = new UploadEvents.AutoEmail().failed("Could not send email " + e.getMessage() , e); - } - finally { - try{ - client.logout(); - client.disconnect(); - } - catch (Exception ignored) { - } - - if(smtpFailureEvent != null){ - smtpFailureEvent.smtpMessages = smtpServerResponses; - if(smtpFailureEvent.smtpMessages.isEmpty()){ - smtpFailureEvent.smtpMessages = new ArrayList<>(Arrays.asList(client.getReplyStrings())); - } - EventBus.getDefault().post(smtpFailureEvent); - } - } - - } - - @Override - protected void onCancel(int cancelReason, @Nullable Throwable throwable) { - LOG.debug("Email job cancelled"); - } - - @Override - protected RetryConstraint shouldReRunOnThrowable(@NonNull Throwable throwable, int runCount, int maxRunCount) { - LOG.error("Could not send email", throwable); - return RetryConstraint.CANCEL; - } - - - /** - * Append the given attachments to the message which is being written by the given writer. - * - * @param boundary separates each file attachment - */ - private static void attachFilesToWriter(Writer writer, String boundary, File[] files) throws IOException { - for (File f : files) { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream((int) f.length()); - FileInputStream inputStream = new FileInputStream(f); - Streams.copyIntoStream(inputStream, outputStream); - - writer.write("--" + boundary + "\n"); - writer.write("Content-Type: application/" + Files.getMimeTypeFromFileName(f.getName()) + "; name=\"" + f.getName() + "\"\n"); - writer.write("Content-Disposition: attachment; filename=\"" + f.getName() + "\"\n"); - writer.write("Content-Transfer-Encoding: base64\n\n"); - String encodedFile = Base64.encodeToString(outputStream.toByteArray(), Base64.DEFAULT); - writer.write(encodedFile); - writer.write("\n"); - } - } - - - private static void checkReply(SMTPClient sc) throws Exception { - if (SMTPReply.isNegativeTransient(sc.getReplyCode())) { - throw new Exception("Transient SMTP error " + sc.getReplyString()); - } else if (SMTPReply.isNegativePermanent(sc.getReplyCode())) { - throw new Exception("Permanent SMTP error " + sc.getReplyString()); - } - } - - - public static String getJobTag(File[] files) { - StringBuilder sb = new StringBuilder(); - for(File f : files){ - sb.append(f.getName()).append("."); - } - return "EMAIL" + sb.toString(); - - } -} From 47e4dce4110c48491b5eb2897e51eb272d7b3ae1 Mon Sep 17 00:00:00 2001 From: mendhak Date: Sun, 17 Mar 2024 12:17:34 +0000 Subject: [PATCH 25/73] Work manager implementation for FTP uploads --- .../gpslogger/senders/ftp/FtpManager.java | 49 ++-- .../gpslogger/senders/ftp/FtpWorker.java | 224 ++++++++++++++++++ 2 files changed, 247 insertions(+), 26 deletions(-) create mode 100644 gpslogger/src/main/java/com/mendhak/gpslogger/senders/ftp/FtpWorker.java diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/ftp/FtpManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/ftp/FtpManager.java index fd4508367..11366bc4d 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/ftp/FtpManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/ftp/FtpManager.java @@ -22,22 +22,25 @@ -import com.birbit.android.jobqueue.CancelResult; -import com.birbit.android.jobqueue.JobManager; -import com.birbit.android.jobqueue.TagConstraint; +import androidx.work.ExistingWorkPolicy; +import androidx.work.OneTimeWorkRequest; +import androidx.work.WorkManager; + import com.mendhak.gpslogger.common.AppSettings; import com.mendhak.gpslogger.common.PreferenceHelper; +import com.mendhak.gpslogger.common.Systems; import com.mendhak.gpslogger.common.events.UploadEvents; import com.mendhak.gpslogger.common.slf4j.Logs; import com.mendhak.gpslogger.loggers.Files; import com.mendhak.gpslogger.senders.FileSender; + import de.greenrobot.event.EventBus; import org.slf4j.Logger; -import java.io.BufferedOutputStream; import java.io.File; -import java.io.FileOutputStream; +import java.util.HashMap; import java.util.List; +import java.util.Objects; public class FtpManager extends FileSender { private static final Logger LOG = Logs.of(FtpManager.class); @@ -54,22 +57,18 @@ public void testFtp(final String servername, final String username, final String try { final File testFile = Files.createTestFile(); - final JobManager jobManager = AppSettings.getJobManager(); + HashMap dataMap = new HashMap() {{ + put("filePath", testFile.getAbsolutePath()); + }}; - jobManager.cancelJobsInBackground(new CancelResult.AsyncCancelCallback() { - @Override - public void onCancelled(CancelResult cancelResult) { - jobManager.addJobInBackground(new FtpJob(servername, port, username, password, directory, - useFtps, protocol, implicit, testFile, testFile.getName())); - } - }, TagConstraint.ANY, FtpJob.getJobTag(testFile)); + String tag = String.valueOf(Objects.hashCode(testFile)) ; + OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(FtpWorker.class, dataMap); + WorkManager.getInstance(AppSettings.getInstance()) + .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); } catch (Exception ex) { EventBus.getDefault().post(new UploadEvents.Ftp().failed(ex.getMessage(), ex)); } - - - } @Override @@ -103,16 +102,14 @@ public String getName() { public void uploadFile(final File f) { - final JobManager jobManager = AppSettings.getJobManager(); - jobManager.cancelJobsInBackground(new CancelResult.AsyncCancelCallback() { - @Override - public void onCancelled(CancelResult cancelResult) { - jobManager.addJobInBackground(new FtpJob(preferenceHelper.getFtpServerName(), preferenceHelper.getFtpPort(), - preferenceHelper.getFtpUsername(), preferenceHelper.getFtpPassword(), preferenceHelper.getFtpDirectory(), - preferenceHelper.shouldFtpUseFtps(), preferenceHelper.getFtpProtocol(), preferenceHelper.isFtpImplicit(), - f, f.getName())); - } - }, TagConstraint.ANY, FtpJob.getJobTag(f)); + HashMap dataMap = new HashMap() {{ + put("filePath", f.getAbsolutePath()); + }}; + + String tag = String.valueOf(Objects.hashCode(f)) ; + OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(FtpWorker.class, dataMap); + WorkManager.getInstance(AppSettings.getInstance()) + .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); } diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/ftp/FtpWorker.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/ftp/FtpWorker.java new file mode 100644 index 000000000..db63cdc9b --- /dev/null +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/ftp/FtpWorker.java @@ -0,0 +1,224 @@ +package com.mendhak.gpslogger.senders.ftp; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.work.Worker; +import androidx.work.WorkerParameters; + +import com.mendhak.gpslogger.common.AppSettings; +import com.mendhak.gpslogger.common.PreferenceHelper; +import com.mendhak.gpslogger.common.Strings; +import com.mendhak.gpslogger.common.events.UploadEvents; +import com.mendhak.gpslogger.common.network.Networks; +import com.mendhak.gpslogger.common.slf4j.LoggingOutputStream; +import com.mendhak.gpslogger.common.slf4j.Logs; + +import org.apache.commons.net.PrintCommandListener; +import org.apache.commons.net.ftp.FTP; +import org.apache.commons.net.ftp.FTPClient; +import org.apache.commons.net.ftp.FTPFile; +import org.apache.commons.net.ftp.FTPSClient; +import org.slf4j.Logger; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; + +import de.greenrobot.event.EventBus; + +public class FtpWorker extends Worker { + + private static final Logger LOG = Logs.of(FtpWorker.class); + static UploadEvents.Ftp jobResult; + static ArrayList ftpServerResponses; + + public FtpWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { + super(context, workerParams); + } + + @NonNull + @Override + public Result doWork() { + ftpServerResponses = new ArrayList<>(); + PreferenceHelper preferenceHelper = PreferenceHelper.getInstance(); + String server = preferenceHelper.getFtpServerName(); + int port = preferenceHelper.getFtpPort(); + String username = preferenceHelper.getFtpUsername(); + String password = preferenceHelper.getFtpPassword(); + boolean useFtps = preferenceHelper.shouldFtpUseFtps(); + String protocol = preferenceHelper.getFtpProtocol(); + boolean implicit = preferenceHelper.isFtpImplicit(); + + String filePath = getInputData().getString("filePath"); + File file = new File(filePath); + + String directory = preferenceHelper.getFtpDirectory(); + + if (upload(server, username, password, directory, port, useFtps, protocol, implicit, file, file.getName())) { + LOG.info("FTP - file uploaded"); + EventBus.getDefault().post(new UploadEvents.Ftp().succeeded()); + return Result.success(); + } else { + jobResult.ftpMessages = ftpServerResponses; + EventBus.getDefault().post(jobResult); + } + + + return Result.success(); + } + + + public synchronized static boolean upload(String server, String username, String password, String directory, int port, + boolean useFtps, String protocol, boolean implicit, + File gpxFile, String fileName) { + FTPClient client; + + try { + if (useFtps) { + client = new FTPSClient(protocol, implicit); + + KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(Networks.getKnownServersStore(AppSettings.getInstance()), null); + KeyManager km = kmf.getKeyManagers()[0]; + + ((FTPSClient) client).setKeyManager(km); + ((FTPSClient) client).setTrustManager(Networks.getTrustManager(AppSettings.getInstance())); + + } else { + client = new FTPClient(); + } + + } catch (Exception e) { + jobResult = new UploadEvents.Ftp().failed( "Could not create FTP Client" , e); + LOG.error("Could not create FTP Client", e); + return false; + } + + + try { + + client.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(new LoggingOutputStream(LOG)))); + client.setControlEncoding("UTF-8"); + client.setDefaultTimeout(60000); + client.setConnectTimeout(60000); + client.connect(server, port); + client.setSoTimeout(60000); + client.setDataTimeout(60000); + logServerReply(client); + + + if (client.login(username, password)) { + + if(useFtps){ + ((FTPSClient)client).execPBSZ(0); + logServerReply(client); + ((FTPSClient)client).execPROT("P"); + logServerReply(client); + } + + + client.enterLocalPassiveMode(); + logServerReply(client); + + LOG.debug("Uploading file to FTP server " + server); + LOG.debug("Checking for FTP directory " + directory); + FTPFile[] existingDirectory = client.listFiles(directory); + logServerReply(client); + + if (existingDirectory.length <= 0) { + LOG.debug("Attempting to create FTP directory " + directory); + ftpCreateDirectoryTree(client, directory); + logServerReply(client); + } + + FileInputStream inputStream = new FileInputStream(gpxFile); + client.changeWorkingDirectory(directory); + client.setFileType(FTP.BINARY_FILE_TYPE); + boolean result = client.storeFile(fileName, inputStream); + inputStream.close(); + logServerReply(client); + if (result) { + LOG.debug("Successfully FTPd file " + fileName); + } else { + jobResult = new UploadEvents.Ftp().failed( "Failed to FTP file " + fileName , null); + LOG.debug("Failed to FTP file " + fileName); + return false; + } + + } else { + logServerReply(client); + jobResult = new UploadEvents.Ftp().failed( "Could not log in to FTP server" , null); + LOG.debug("Could not log in to FTP server"); + return false; + } + + } catch (Exception e) { + logServerReply(client); + jobResult = new UploadEvents.Ftp().failed( "Could not connect or upload to FTP server.", e); + LOG.error("Could not connect or upload to FTP server.", e); + return false; + } finally { + try { + client.logout(); + logServerReply(client); + + client.disconnect(); + logServerReply(client); + } catch (Exception e) { + if(jobResult == null){ + jobResult = new UploadEvents.Ftp().failed( "Could not logout or disconnect", e); + } + + LOG.error("Could not logout or disconnect", e); + return false; + } + } + + return true; + } + + private static void ftpCreateDirectoryTree(FTPClient client, String dirTree) throws IOException { + + boolean dirExists = true; + + //tokenize the string and attempt to change into each directory level. If you cannot, then start creating. + String[] directories = dirTree.split("/"); + for (String dir : directories) { + if (dir.length() > 0) { + if (dirExists) { + dirExists = client.changeWorkingDirectory(dir); + logServerReply(client); + } + if (!dirExists) { + client.makeDirectory(dir); + logServerReply(client); + client.changeWorkingDirectory(dir); + logServerReply(client); + } + } + } + } + + + private static void logServerReply(FTPClient client) { + String singleReply = client.getReplyString(); + if(!Strings.isNullOrEmpty(singleReply)){ + ftpServerResponses.add(singleReply); + } + + String[] replies = client.getReplyStrings(); + if (replies != null && replies.length > 0) { + for (String aReply : replies) { + if(!Strings.isNullOrEmpty(aReply)){ + ftpServerResponses.add(aReply); + } + } + } + } +} From 105889815a88a22f689c6f9223b20d9cff740635 Mon Sep 17 00:00:00 2001 From: mendhak Date: Sun, 17 Mar 2024 12:23:05 +0000 Subject: [PATCH 26/73] Remove unused arguments --- .../com/mendhak/gpslogger/senders/ftp/FtpManager.java | 3 ++- .../gpslogger/ui/fragments/settings/FtpFragment.java | 8 ++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/ftp/FtpManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/ftp/FtpManager.java index 11366bc4d..682fb5efd 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/ftp/FtpManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/ftp/FtpManager.java @@ -51,7 +51,7 @@ public FtpManager(PreferenceHelper preferenceHelper) { this.preferenceHelper = preferenceHelper; } - public void testFtp(final String servername, final String username, final String password, final String directory, final int port, final boolean useFtps, final String protocol, final boolean implicit) { + public void testFtp() { try { @@ -76,6 +76,7 @@ public void uploadFile(List files) { if (!validSettings(preferenceHelper.getFtpServerName(), preferenceHelper.getFtpUsername(), preferenceHelper.getFtpPassword(), preferenceHelper.getFtpPort(), preferenceHelper.shouldFtpUseFtps(), preferenceHelper.getFtpProtocol(), preferenceHelper.isFtpImplicit())) { EventBus.getDefault().post(new UploadEvents.Ftp().failed()); + return; } for (File f : files) { diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/FtpFragment.java b/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/FtpFragment.java index a398f0848..5a71cfdb8 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/FtpFragment.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/FtpFragment.java @@ -181,7 +181,7 @@ public boolean onPreferenceClick(Preference preference) { boolean useFtpsPreference = preferenceHelper.shouldFtpUseFtps(); String sslTlsPreference = preferenceHelper.getFtpProtocol(); boolean implicitPreference = preferenceHelper.isFtpImplicit(); - String directoryPreference = preferenceHelper.getFtpDirectory(); + if (!helper.validSettings(servernamePreference, usernamePreference, passwordPreference, portPreference, @@ -194,11 +194,7 @@ public boolean onPreferenceClick(Preference preference) { } Dialogs.progress((FragmentActivity) getActivity(), getString(R.string.autoftp_testing)); - - - helper.testFtp(servernamePreference, usernamePreference, passwordPreference, - directoryPreference, portPreference, useFtpsPreference, - sslTlsPreference, implicitPreference); + helper.testFtp(); } From c61eab9da6674fd9370435fd0a57645774334ea5 Mon Sep 17 00:00:00 2001 From: mendhak Date: Sun, 17 Mar 2024 12:23:33 +0000 Subject: [PATCH 27/73] Remove unused FTP Job --- .../mendhak/gpslogger/senders/ftp/FtpJob.java | 271 ------------------ 1 file changed, 271 deletions(-) delete mode 100644 gpslogger/src/main/java/com/mendhak/gpslogger/senders/ftp/FtpJob.java diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/ftp/FtpJob.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/ftp/FtpJob.java deleted file mode 100644 index 9e4e34374..000000000 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/ftp/FtpJob.java +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright (C) 2016 mendhak - * - * This file is part of GPSLogger for Android. - * - * GPSLogger for Android is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * GPSLogger for Android is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GPSLogger for Android. If not, see . - */ - -package com.mendhak.gpslogger.senders.ftp; - - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.birbit.android.jobqueue.Job; -import com.birbit.android.jobqueue.Params; -import com.birbit.android.jobqueue.RetryConstraint; -import com.mendhak.gpslogger.common.AppSettings; -import com.mendhak.gpslogger.common.network.Networks; -import com.mendhak.gpslogger.common.Strings; -import com.mendhak.gpslogger.common.events.UploadEvents; -import com.mendhak.gpslogger.common.slf4j.LoggingOutputStream; -import com.mendhak.gpslogger.common.slf4j.Logs; -import de.greenrobot.event.EventBus; -import org.apache.commons.net.PrintCommandListener; -import org.apache.commons.net.ftp.FTP; -import org.apache.commons.net.ftp.FTPClient; -import org.apache.commons.net.ftp.FTPFile; -import org.apache.commons.net.ftp.FTPSClient; -import org.slf4j.Logger; - -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.ArrayList; - - -public class FtpJob extends Job { - - private static final Logger LOG = Logs.of(FtpJob.class); - - String server; - int port; - String username; - String password; - boolean useFtps; - String protocol; - boolean implicit; - File gpxFile; - String fileName; - String directory; - - static UploadEvents.Ftp jobResult; - static ArrayList ftpServerResponses; - - protected FtpJob(String server, int port, String username, - String password, String directory, boolean useFtps, String protocol, boolean implicit, - File gpxFile, String fileName) { - super(new Params(1).requireNetwork().persist().addTags(getJobTag(gpxFile))); - - this.server = server; - this.port = port; - this.username = username; - this.password = password; - this.useFtps = useFtps; - this.protocol = protocol; - this.implicit = implicit; - this.gpxFile = gpxFile; - this.fileName = fileName; - this.directory = directory; - - ftpServerResponses = new ArrayList<>(); - jobResult = null; - - } - - public synchronized static boolean upload(String server, String username, String password, String directory, int port, - boolean useFtps, String protocol, boolean implicit, - File gpxFile, String fileName) { - FTPClient client; - - try { - if (useFtps) { - client = new FTPSClient(protocol, implicit); - - KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - kmf.init(Networks.getKnownServersStore(AppSettings.getInstance()), null); - KeyManager km = kmf.getKeyManagers()[0]; - - ((FTPSClient) client).setKeyManager(km); - ((FTPSClient) client).setTrustManager(Networks.getTrustManager(AppSettings.getInstance())); - - } else { - client = new FTPClient(); - } - - } catch (Exception e) { - jobResult = new UploadEvents.Ftp().failed( "Could not create FTP Client" , e); - LOG.error("Could not create FTP Client", e); - return false; - } - - - try { - - client.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(new LoggingOutputStream(LOG)))); - client.setControlEncoding("UTF-8"); - client.setDefaultTimeout(60000); - client.setConnectTimeout(60000); - client.connect(server, port); - client.setSoTimeout(60000); - client.setDataTimeout(60000); - logServerReply(client); - - - if (client.login(username, password)) { - - if(useFtps){ - ((FTPSClient)client).execPBSZ(0); - logServerReply(client); - ((FTPSClient)client).execPROT("P"); - logServerReply(client); - } - - - client.enterLocalPassiveMode(); - logServerReply(client); - - LOG.debug("Uploading file to FTP server " + server); - LOG.debug("Checking for FTP directory " + directory); - FTPFile[] existingDirectory = client.listFiles(directory); - logServerReply(client); - - if (existingDirectory.length <= 0) { - LOG.debug("Attempting to create FTP directory " + directory); - ftpCreateDirectoryTree(client, directory); - logServerReply(client); - } - - FileInputStream inputStream = new FileInputStream(gpxFile); - client.changeWorkingDirectory(directory); - client.setFileType(FTP.BINARY_FILE_TYPE); - boolean result = client.storeFile(fileName, inputStream); - inputStream.close(); - logServerReply(client); - if (result) { - LOG.debug("Successfully FTPd file " + fileName); - } else { - jobResult = new UploadEvents.Ftp().failed( "Failed to FTP file " + fileName , null); - LOG.debug("Failed to FTP file " + fileName); - return false; - } - - } else { - logServerReply(client); - jobResult = new UploadEvents.Ftp().failed( "Could not log in to FTP server" , null); - LOG.debug("Could not log in to FTP server"); - return false; - } - - } catch (Exception e) { - logServerReply(client); - jobResult = new UploadEvents.Ftp().failed( "Could not connect or upload to FTP server.", e); - LOG.error("Could not connect or upload to FTP server.", e); - return false; - } finally { - try { - client.logout(); - logServerReply(client); - - client.disconnect(); - logServerReply(client); - } catch (Exception e) { - if(jobResult == null){ - jobResult = new UploadEvents.Ftp().failed( "Could not logout or disconnect", e); - } - - LOG.error("Could not logout or disconnect", e); - return false; - } - } - - return true; - } - - - private static void ftpCreateDirectoryTree(FTPClient client, String dirTree) throws IOException { - - boolean dirExists = true; - - //tokenize the string and attempt to change into each directory level. If you cannot, then start creating. - String[] directories = dirTree.split("/"); - for (String dir : directories) { - if (dir.length() > 0) { - if (dirExists) { - dirExists = client.changeWorkingDirectory(dir); - logServerReply(client); - } - if (!dirExists) { - client.makeDirectory(dir); - logServerReply(client); - client.changeWorkingDirectory(dir); - logServerReply(client); - } - } - } - } - - - private static void logServerReply(FTPClient client) { - String singleReply = client.getReplyString(); - if(!Strings.isNullOrEmpty(singleReply)){ - ftpServerResponses.add(singleReply); - } - - String[] replies = client.getReplyStrings(); - if (replies != null && replies.length > 0) { - for (String aReply : replies) { - if(!Strings.isNullOrEmpty(aReply)){ - ftpServerResponses.add(aReply); - } - } - } - } - - @Override - public void onAdded() { - - } - - @Override - public void onRun() throws Throwable { - if (upload(server, username, password, directory, port, useFtps, protocol, implicit, gpxFile, fileName)) { - LOG.info("FTP - file uploaded"); - EventBus.getDefault().post(new UploadEvents.Ftp().succeeded()); - } else { - jobResult.ftpMessages = ftpServerResponses; - EventBus.getDefault().post(jobResult); - } - } - - @Override - protected void onCancel(int cancelReason, @Nullable Throwable throwable) { - - } - - @Override - protected RetryConstraint shouldReRunOnThrowable(@NonNull Throwable throwable, int runCount, int maxRunCount) { - EventBus.getDefault().post(new UploadEvents.Ftp().failed("Could not FTP file", throwable)); - LOG.error("Could not FTP file", throwable); - return RetryConstraint.CANCEL; - } - - public static String getJobTag(File testFile) { - return "FTP"+testFile.getName(); - } -} From 65e54dc33808ff6fff6a5f21486971df5aced7e4 Mon Sep 17 00:00:00 2001 From: mendhak Date: Sun, 17 Mar 2024 13:51:42 +0000 Subject: [PATCH 28/73] Google Drive upload using WorkManager --- .../googledrive/GoogleDriveManager.java | 30 +- .../googledrive/GoogleDriveWorker.java | 262 ++++++++++++++++++ .../settings/GoogleDriveSettingsFragment.java | 2 +- 3 files changed, 281 insertions(+), 13 deletions(-) create mode 100644 gpslogger/src/main/java/com/mendhak/gpslogger/senders/googledrive/GoogleDriveWorker.java diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/googledrive/GoogleDriveManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/googledrive/GoogleDriveManager.java index 8b29d99cd..cb7c73135 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/googledrive/GoogleDriveManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/googledrive/GoogleDriveManager.java @@ -3,12 +3,14 @@ import android.content.Context; import android.net.Uri; -import com.birbit.android.jobqueue.CancelResult; -import com.birbit.android.jobqueue.JobManager; -import com.birbit.android.jobqueue.TagConstraint; +import androidx.work.ExistingWorkPolicy; +import androidx.work.OneTimeWorkRequest; +import androidx.work.WorkManager; + import com.mendhak.gpslogger.common.AppSettings; import com.mendhak.gpslogger.common.PreferenceHelper; import com.mendhak.gpslogger.common.Strings; +import com.mendhak.gpslogger.common.Systems; import com.mendhak.gpslogger.common.slf4j.Logs; import com.mendhak.gpslogger.senders.FileSender; @@ -21,7 +23,9 @@ import org.slf4j.Logger; import java.io.File; +import java.util.HashMap; import java.util.List; +import java.util.Objects; public class GoogleDriveManager extends FileSender { @@ -81,18 +85,20 @@ public static AuthState getAuthState() { public void uploadFile(List files) { for (File f : files) { LOG.debug(f.getName()); - uploadFile(f.getName()); + uploadFile(f); } } - public void uploadFile(String fileName) { - final JobManager jobManager = AppSettings.getJobManager(); - jobManager.cancelJobsInBackground(new CancelResult.AsyncCancelCallback() { - @Override - public void onCancelled(CancelResult cancelResult) { - jobManager.addJobInBackground(new GoogleDriveJob(fileName)); - } - }, TagConstraint.ANY, GoogleDriveJob.getJobTag(fileName)); + public void uploadFile(File fileToUpload) { + + String tag = String.valueOf(Objects.hashCode(fileToUpload)); + HashMap dataMap = new HashMap() {{ + put("filePath", fileToUpload.getAbsolutePath()); + }}; + + OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(GoogleDriveWorker.class, dataMap); + WorkManager.getInstance(AppSettings.getInstance()) + .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); } @Override diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/googledrive/GoogleDriveWorker.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/googledrive/GoogleDriveWorker.java new file mode 100644 index 000000000..2ba339648 --- /dev/null +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/googledrive/GoogleDriveWorker.java @@ -0,0 +1,262 @@ +package com.mendhak.gpslogger.senders.googledrive; + +import android.content.Context; +import android.os.Build; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.work.Worker; +import androidx.work.WorkerParameters; + +import com.mendhak.gpslogger.common.AppSettings; +import com.mendhak.gpslogger.common.PreferenceHelper; +import com.mendhak.gpslogger.common.Strings; +import com.mendhak.gpslogger.common.events.UploadEvents; +import com.mendhak.gpslogger.common.slf4j.Logs; +import com.mendhak.gpslogger.loggers.Files; +import com.mendhak.gpslogger.loggers.Streams; + +import net.openid.appauth.AuthState; +import net.openid.appauth.AuthorizationException; +import net.openid.appauth.AuthorizationService; + +import org.json.JSONObject; +import org.slf4j.Logger; + +import java.io.File; +import java.io.FileInputStream; +import java.net.URLEncoder; +import java.util.concurrent.atomic.AtomicBoolean; + +import de.greenrobot.event.EventBus; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; + +public class GoogleDriveWorker extends Worker { + private static final Logger LOG = Logs.of(GoogleDriveWorker.class); + + private String googleDriveAccessToken; + + + public GoogleDriveWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { + super(context, workerParams); + } + + @NonNull + @Override + public Result doWork() { + + String filePath = getInputData().getString("filePath"); + File fileToUpload = new File(filePath); + boolean success = true; + String failureMessage = ""; + Throwable failureThrowable = null; + + + AuthState authState = GoogleDriveManager.getAuthState(); + if (!authState.isAuthorized()) { + EventBus.getDefault().post(new UploadEvents.GoogleDrive().failed("Could not upload to Google Drive. Not Authorized.")); + } + + final AtomicBoolean taskDone = new AtomicBoolean(false); + PreferenceHelper preferenceHelper = PreferenceHelper.getInstance(); + + try { + AuthorizationService authorizationService = GoogleDriveManager.getAuthorizationService(AppSettings.getInstance()); + + // The performActionWithFreshTokens seems to happen on a UI thread! (Why??) + // So I can't do network calls on this thread. + // Instead, updating a class level variable, and waiting for it afterwards. + // https://github.com/openid/AppAuth-Android/issues/123 + authState.performActionWithFreshTokens(authorizationService, new AuthState.AuthStateAction() { + @Override + public void execute(@Nullable String accessToken, @Nullable String idToken, @Nullable AuthorizationException ex) { + if (ex != null) { + EventBus.getDefault().post(new UploadEvents.GoogleDrive().failed(ex.toJsonString(), ex)); + taskDone.set(true); + LOG.error(ex.toJsonString(), ex); + return; + } + googleDriveAccessToken = accessToken; + taskDone.set(true); + } + }); + + // Wait for the performActionWithFreshTokens.execute callback + // (which happens on the UI thread for some reason) to complete. + while (!taskDone.get()) { + Thread.sleep(500); + } + + if (Strings.isNullOrEmpty(googleDriveAccessToken)) { + LOG.error("Failed to fetch Access Token for Google Drive. Stopping this job."); + return Result.failure(); + } + + // Figure out the Folder ID to upload to, from the path; recursively create if it doesn't exist. + String folderPath = preferenceHelper.getGoogleDriveFolderPath(); + String[] pathParts = folderPath.split("/"); + String parentFolderId = null; + String latestFolderId = null; + for (String part : pathParts) { + latestFolderId = getFileIdFromFileName(googleDriveAccessToken, part, parentFolderId); + if (!Strings.isNullOrEmpty(latestFolderId)) { + LOG.debug("Folder " + part + " found, folder ID is " + latestFolderId); + } else { + LOG.debug("Folder " + part + " not found, creating."); + latestFolderId = createEmptyFile(googleDriveAccessToken, part, + "application/vnd.google-apps.folder", Strings.isNullOrEmpty(parentFolderId) ? "root" : parentFolderId); + } + parentFolderId = latestFolderId; + } + + String gpsLoggerFolderId = latestFolderId; + + if (Strings.isNullOrEmpty(gpsLoggerFolderId)) { + failureMessage = "Could not create folder"; + success = false; + } + else { + // Now search for the file + String gpxFileId = getFileIdFromFileName(googleDriveAccessToken, fileToUpload.getName(), gpsLoggerFolderId); + + if (Strings.isNullOrEmpty(gpxFileId)) { + LOG.debug("Creating an empty file first."); + gpxFileId = createEmptyFile(googleDriveAccessToken, fileToUpload.getName(), Files.getMimeTypeFromFileName(fileToUpload.getName()), gpsLoggerFolderId); + + if (Strings.isNullOrEmpty(gpxFileId)) { + failureMessage = "Could not create file"; + success = false; + } + } + + // The above empty file creation needs to happen first - this shouldn't be an 'else' to the above if. + if (!Strings.isNullOrEmpty(gpxFileId)) { + LOG.debug("Uploading file contents"); + updateFileContents(googleDriveAccessToken, gpxFileId, fileToUpload); + } + + } + + } catch (Exception e) { + LOG.error(e.getMessage(), e); + success = false; + failureMessage = e.getMessage(); + failureThrowable = e; + } + + if(success){ + EventBus.getDefault().post(new UploadEvents.GoogleDrive().succeeded()); + return Result.success(); + } + + if(getRunAttemptCount() < getRetryLimit()){ + LOG.warn(String.format("Google Drive - attempt %d of %d failed, will retry", getRunAttemptCount(), getRetryLimit())); + return Result.retry(); + } + + if(failureThrowable == null) { + failureThrowable = new Exception(failureMessage); + } + + EventBus.getDefault() + .post(new UploadEvents.GoogleDrive().failed(failureMessage, failureThrowable)); + return Result.failure(); + + } + + private String getFileIdFromFileName(String accessToken, String fileName, String inFolderId) throws Exception { + String fileId = ""; + fileName = URLEncoder.encode(fileName, "UTF-8"); + + String inFolderParam = ""; + if (!Strings.isNullOrEmpty(inFolderId)) { + inFolderParam = "+and+'" + inFolderId + "'+in+parents"; + } + String searchUrl = "https://www.googleapis.com/drive/v3/files?q=name%20%3D%20%27" + fileName + "%27%20and%20trashed%20%3D%20false" + inFolderParam; + OkHttpClient client = new OkHttpClient(); + Request.Builder requestBuilder = new Request.Builder().url(searchUrl); + + requestBuilder.addHeader("Authorization", "Bearer " + accessToken); + + Request request = requestBuilder.build(); + Response response = client.newCall(request).execute(); + String fileMetadata = response.body().string(); + LOG.debug(fileMetadata); + response.body().close(); + JSONObject fileMetadataJson = new JSONObject(fileMetadata); + if (fileMetadataJson.getJSONArray("files") != null && fileMetadataJson.getJSONArray("files").length() > 0) { + fileId = fileMetadataJson.getJSONArray("files").getJSONObject(0).get("id").toString(); + LOG.debug("Found file with ID " + fileId); + } + + return fileId; + } + + private String createEmptyFile(String accessToken, String fileName, String mimeType, String parentFolderId) throws Exception { + + String fileId = null; + String createFileUrl = "https://www.googleapis.com/drive/v3/files"; + + String createFilePayload = " {\n" + + " \"name\": \"" + fileName + "\",\n" + + " \"mimeType\": \"" + mimeType + "\",\n" + + " \"parents\": [\"" + parentFolderId + "\"]\n" + + " }"; + + + OkHttpClient client = new OkHttpClient(); + Request.Builder requestBuilder = new Request.Builder().url(createFileUrl); + + requestBuilder.addHeader("Authorization", "Bearer " + accessToken); + RequestBody body = RequestBody.create(MediaType.parse("application/json"), createFilePayload); + requestBuilder = requestBuilder.method("POST", body); + + + Request request = requestBuilder.build(); + Response response = client.newCall(request).execute(); + String fileMetadata = response.body().string(); + LOG.debug(fileMetadata); + response.body().close(); + + JSONObject fileMetadataJson = new JSONObject(fileMetadata); + fileId = fileMetadataJson.getString("id"); + + return fileId; + } + + private String updateFileContents(String accessToken, String gpxFileId, File fileToUpload) throws Exception { + FileInputStream fis = new FileInputStream(fileToUpload); + String fileId = null; + + String fileUpdateUrl = "https://www.googleapis.com/upload/drive/v3/files/" + gpxFileId + "?uploadType=media"; + + OkHttpClient client = new OkHttpClient(); + Request.Builder requestBuilder = new Request.Builder().url(fileUpdateUrl); + + requestBuilder.addHeader("Authorization", "Bearer " + accessToken); + RequestBody body = RequestBody.create(MediaType.parse(Files.getMimeTypeFromFileName(fileToUpload.getName())), Streams.getByteArrayFromInputStream(fis)); + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { + requestBuilder.addHeader("X-HTTP-Method-Override", "PATCH"); + } + requestBuilder = requestBuilder.method("PATCH", body); + + Request request = requestBuilder.build(); + Response response = client.newCall(request).execute(); + String fileMetadata = response.body().string(); + LOG.debug(fileMetadata); + response.body().close(); + + JSONObject fileMetadataJson = new JSONObject(fileMetadata); + fileId = fileMetadataJson.getString("id"); + + return fileId; + } + + protected int getRetryLimit() { + return 3; + } +} diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/GoogleDriveSettingsFragment.java b/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/GoogleDriveSettingsFragment.java index cfb039ac8..d15707d3c 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/GoogleDriveSettingsFragment.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/GoogleDriveSettingsFragment.java @@ -243,7 +243,7 @@ private void uploadTestFile() { try { File testFile = Files.createTestFile(); - manager.uploadFile(testFile.getName()); + manager.uploadFile(testFile); } catch (Exception ex) { LOG.error("Could not create local test file", ex); From db209ebc5f7e45dce542e284f217452038397bf1 Mon Sep 17 00:00:00 2001 From: mendhak Date: Sun, 17 Mar 2024 13:52:10 +0000 Subject: [PATCH 29/73] Remove unused Google Drive Job --- .../senders/googledrive/GoogleDriveJob.java | 261 ------------------ 1 file changed, 261 deletions(-) delete mode 100644 gpslogger/src/main/java/com/mendhak/gpslogger/senders/googledrive/GoogleDriveJob.java diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/googledrive/GoogleDriveJob.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/googledrive/GoogleDriveJob.java deleted file mode 100644 index d274fbed1..000000000 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/googledrive/GoogleDriveJob.java +++ /dev/null @@ -1,261 +0,0 @@ -package com.mendhak.gpslogger.senders.googledrive; - -import android.os.Build; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.birbit.android.jobqueue.Job; -import com.birbit.android.jobqueue.Params; -import com.birbit.android.jobqueue.RetryConstraint; -import com.mendhak.gpslogger.common.AppSettings; -import com.mendhak.gpslogger.common.PreferenceHelper; -import com.mendhak.gpslogger.common.Strings; -import com.mendhak.gpslogger.common.events.UploadEvents; -import com.mendhak.gpslogger.common.slf4j.Logs; -import com.mendhak.gpslogger.loggers.Files; -import com.mendhak.gpslogger.loggers.Streams; - -import net.openid.appauth.AuthState; -import net.openid.appauth.AuthorizationException; -import net.openid.appauth.AuthorizationService; - -import org.json.JSONObject; -import org.slf4j.Logger; - -import java.io.File; -import java.io.FileInputStream; -import java.net.URLEncoder; -import java.util.concurrent.atomic.AtomicBoolean; - -import de.greenrobot.event.EventBus; -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; - -public class GoogleDriveJob extends Job { - - private static final Logger LOG = Logs.of(GoogleDriveJob.class); - private static final PreferenceHelper preferenceHelper = PreferenceHelper.getInstance(); - private final AtomicBoolean taskDone = new AtomicBoolean(false); - private final String fileName; - private String googleDriveAccessToken; - - protected GoogleDriveJob(String fileName) { - super(new Params(1).requireNetwork().persist().addTags(getJobTag(fileName)).groupBy("GoogleDrive")); - this.fileName = fileName; - } - - public static String getJobTag(String fileName) { - return "GOOGLEDRIVE" + fileName; - } - - @Override - public void onAdded() { - LOG.debug("Google Drive job added"); - } - - @Override - public void onRun() throws Throwable { - File gpsDir = new File(preferenceHelper.getGpsLoggerFolder()); - AuthState authState = GoogleDriveManager.getAuthState(); - if (!authState.isAuthorized()) { - EventBus.getDefault().post(new UploadEvents.GoogleDrive().failed("Could not upload to Google Drive. Not Authorized.")); - } - - AuthorizationService authorizationService = GoogleDriveManager.getAuthorizationService(AppSettings.getInstance()); - - // The performActionWithFreshTokens seems to happen on a UI thread! (Why??) - // So I can't do network calls on this thread. - // Instead, updating a class level variable, and waiting for it afterwards. - // https://github.com/openid/AppAuth-Android/issues/123 - authState.performActionWithFreshTokens(authorizationService, new AuthState.AuthStateAction() { - @Override - public void execute(@Nullable String accessToken, @Nullable String idToken, @Nullable AuthorizationException ex) { - if (ex != null) { - EventBus.getDefault().post(new UploadEvents.GoogleDrive().failed(ex.toJsonString(), ex)); - taskDone.set(true); - LOG.error(ex.toJsonString(), ex); - return; - } - googleDriveAccessToken = accessToken; - taskDone.set(true); - } - }); - - // Wait for the performActionWithFreshTokens.execute callback - // (which happens on the UI thread for some reason) to complete. - while (!taskDone.get()) { - Thread.sleep(500); - } - - if (Strings.isNullOrEmpty(googleDriveAccessToken)) { - LOG.error("Failed to fetch Access Token for Google Drive. Stopping this job."); - return; - } - - - try { - - // Figure out the Folder ID to upload to, from the path; recursively create if it doesn't exist. - String folderPath = preferenceHelper.getGoogleDriveFolderPath(); - String[] pathParts = folderPath.split("/"); - String parentFolderId = null; - String latestFolderId = null; - for (String part : pathParts) { - latestFolderId = getFileIdFromFileName(googleDriveAccessToken, part, parentFolderId); - if (!Strings.isNullOrEmpty(latestFolderId)) { - LOG.debug("Folder " + part + " found, folder ID is " + latestFolderId); - } else { - LOG.debug("Folder " + part + " not found, creating."); - latestFolderId = createEmptyFile(googleDriveAccessToken, part, - "application/vnd.google-apps.folder", Strings.isNullOrEmpty(parentFolderId) ? "root" : parentFolderId); - } - parentFolderId = latestFolderId; - } - - String gpsLoggerFolderId = latestFolderId; - - if (Strings.isNullOrEmpty(gpsLoggerFolderId)) { - EventBus.getDefault().post(new UploadEvents.GoogleDrive().failed("Could not create folder")); - return; - } - - // Now search for the file - String gpxFileId = getFileIdFromFileName(googleDriveAccessToken, fileName, gpsLoggerFolderId); - - if (Strings.isNullOrEmpty(gpxFileId)) { - LOG.debug("Creating an empty file first."); - gpxFileId = createEmptyFile(googleDriveAccessToken, fileName, Files.getMimeTypeFromFileName(fileName), gpsLoggerFolderId); - - if (Strings.isNullOrEmpty(gpxFileId)) { - EventBus.getDefault().post(new UploadEvents.GoogleDrive().failed("Could not create file")); - return; - } - } - - // Upload contents to file - if (!Strings.isNullOrEmpty(gpxFileId)) { - LOG.debug("Uploading file contents"); - updateFileContents(googleDriveAccessToken, gpxFileId, fileName); - } - - EventBus.getDefault().post(new UploadEvents.GoogleDrive().succeeded()); - - } catch (Exception e) { - LOG.error(e.getMessage(), e); - EventBus.getDefault().post(new UploadEvents.GoogleDrive().failed(e.getMessage(), e)); - } - - } - - private String getFileIdFromFileName(String accessToken, String fileName, String inFolderId) throws Exception { - String fileId = ""; - fileName = URLEncoder.encode(fileName, "UTF-8"); - - String inFolderParam = ""; - if (!Strings.isNullOrEmpty(inFolderId)) { - inFolderParam = "+and+'" + inFolderId + "'+in+parents"; - } - String searchUrl = "https://www.googleapis.com/drive/v3/files?q=name%20%3D%20%27" + fileName + "%27%20and%20trashed%20%3D%20false" + inFolderParam; - OkHttpClient client = new OkHttpClient(); - Request.Builder requestBuilder = new Request.Builder().url(searchUrl); - - requestBuilder.addHeader("Authorization", "Bearer " + accessToken); - - Request request = requestBuilder.build(); - Response response = client.newCall(request).execute(); - String fileMetadata = response.body().string(); - LOG.debug(fileMetadata); - response.body().close(); - JSONObject fileMetadataJson = new JSONObject(fileMetadata); - if (fileMetadataJson.getJSONArray("files") != null && fileMetadataJson.getJSONArray("files").length() > 0) { - fileId = fileMetadataJson.getJSONArray("files").getJSONObject(0).get("id").toString(); - LOG.debug("Found file with ID " + fileId); - } - - return fileId; - } - - private String createEmptyFile(String accessToken, String fileName, String mimeType, String parentFolderId) throws Exception { - - String fileId = null; - String createFileUrl = "https://www.googleapis.com/drive/v3/files"; - - String createFilePayload = " {\n" + - " \"name\": \"" + fileName + "\",\n" + - " \"mimeType\": \"" + mimeType + "\",\n" + - " \"parents\": [\"" + parentFolderId + "\"]\n" + - " }"; - - - OkHttpClient client = new OkHttpClient(); - Request.Builder requestBuilder = new Request.Builder().url(createFileUrl); - - requestBuilder.addHeader("Authorization", "Bearer " + accessToken); - RequestBody body = RequestBody.create(MediaType.parse("application/json"), createFilePayload); - requestBuilder = requestBuilder.method("POST", body); - - - Request request = requestBuilder.build(); - Response response = client.newCall(request).execute(); - String fileMetadata = response.body().string(); - LOG.debug(fileMetadata); - response.body().close(); - - JSONObject fileMetadataJson = new JSONObject(fileMetadata); - fileId = fileMetadataJson.getString("id"); - - return fileId; - } - - private String updateFileContents(String accessToken, String gpxFileId, String fileName) throws Exception { - FileInputStream fis = new FileInputStream(new File(preferenceHelper.getGpsLoggerFolder(), fileName)); - String fileId = null; - - String fileUpdateUrl = "https://www.googleapis.com/upload/drive/v3/files/" + gpxFileId + "?uploadType=media"; - - OkHttpClient client = new OkHttpClient(); - Request.Builder requestBuilder = new Request.Builder().url(fileUpdateUrl); - - requestBuilder.addHeader("Authorization", "Bearer " + accessToken); - RequestBody body = RequestBody.create(MediaType.parse(Files.getMimeTypeFromFileName(fileName)), Streams.getByteArrayFromInputStream(fis)); - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { - requestBuilder.addHeader("X-HTTP-Method-Override", "PATCH"); - } - requestBuilder = requestBuilder.method("PATCH", body); - - Request request = requestBuilder.build(); - Response response = client.newCall(request).execute(); - String fileMetadata = response.body().string(); - LOG.debug(fileMetadata); - response.body().close(); - - JSONObject fileMetadataJson = new JSONObject(fileMetadata); - fileId = fileMetadataJson.getString("id"); - - return fileId; - } - - @Override - protected void onCancel(int cancelReason, @Nullable Throwable throwable) { - EventBus.getDefault().post(new UploadEvents.GoogleDrive().failed("Could not send to Google Drive", throwable)); - LOG.error("Google Drive: maximum attempts failed, giving up", throwable); - } - - @Override - protected RetryConstraint shouldReRunOnThrowable(@NonNull Throwable throwable, int runCount, int maxRunCount) { - EventBus.getDefault().post(new UploadEvents.GoogleDrive().failed("Could not upload to Google Drive", throwable)); - LOG.error("Could not upload to Google Drive", throwable); - return RetryConstraint.CANCEL; - } - - @Override - protected int getRetryLimit() { - return 3; - } - - -} From a317cee1d3a64ba6d86735af8d7a4768383494d5 Mon Sep 17 00:00:00 2001 From: mendhak Date: Sun, 17 Mar 2024 15:20:52 +0000 Subject: [PATCH 30/73] Openstreetmap WorkManager --- .../senders/osm/OpenStreetMapManager.java | 30 ++- .../senders/osm/OpenStreetMapWorker.java | 178 ++++++++++++++++++ 2 files changed, 191 insertions(+), 17 deletions(-) create mode 100644 gpslogger/src/main/java/com/mendhak/gpslogger/senders/osm/OpenStreetMapWorker.java diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/osm/OpenStreetMapManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/osm/OpenStreetMapManager.java index 612b6a713..290a8d33f 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/osm/OpenStreetMapManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/osm/OpenStreetMapManager.java @@ -22,12 +22,13 @@ import android.content.Context; import android.net.Uri; -import com.birbit.android.jobqueue.CancelResult; -import com.birbit.android.jobqueue.JobManager; -import com.birbit.android.jobqueue.TagConstraint; +import androidx.work.OneTimeWorkRequest; +import androidx.work.WorkManager; + import com.mendhak.gpslogger.common.AppSettings; import com.mendhak.gpslogger.common.PreferenceHelper; import com.mendhak.gpslogger.common.Strings; +import com.mendhak.gpslogger.common.Systems; import com.mendhak.gpslogger.common.slf4j.Logs; import com.mendhak.gpslogger.senders.FileSender; @@ -39,7 +40,9 @@ import org.json.JSONException; import org.slf4j.Logger; import java.io.File; +import java.util.HashMap; import java.util.List; +import java.util.Objects; public class OpenStreetMapManager extends FileSender { @@ -47,7 +50,6 @@ public class OpenStreetMapManager extends FileSender { private static final Logger LOG = Logs.of(OpenStreetMapManager.class); - static final String OSM_GPSTRACE_URL = "https://www.openstreetmap.org/api/0.6/gpx/create"; private PreferenceHelper preferenceHelper; public OpenStreetMapManager(PreferenceHelper preferenceHelper) { @@ -128,21 +130,15 @@ public String getName() { public void uploadFile(String fileName) { File gpxFolder = new File(preferenceHelper.getGpsLoggerFolder()); final File chosenFile = new File(gpxFolder, fileName); + String tag = String.valueOf(Objects.hashCode(chosenFile)); - final String gpsTraceUrl = OSM_GPSTRACE_URL; - + HashMap dataMap = new HashMap() {{ + put("filePath", chosenFile.getAbsolutePath()); + }}; - final String description = preferenceHelper.getOSMDescription(); - final String tags = preferenceHelper.getOSMTags(); - final String visibility = preferenceHelper.getOSMVisibility(); - - final JobManager jobManager = AppSettings.getJobManager(); - jobManager.cancelJobsInBackground(new CancelResult.AsyncCancelCallback() { - @Override - public void onCancelled(CancelResult cancelResult) { - jobManager.addJobInBackground(new OSMJob(gpsTraceUrl, chosenFile, description, tags, visibility)); - } - }, TagConstraint.ANY, OSMJob.getJobTag(chosenFile)); + OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(OpenStreetMapWorker.class, dataMap); + WorkManager.getInstance(AppSettings.getInstance()) + .enqueueUniqueWork(tag, androidx.work.ExistingWorkPolicy.REPLACE, workRequest); } diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/osm/OpenStreetMapWorker.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/osm/OpenStreetMapWorker.java new file mode 100644 index 000000000..c8f38d4ec --- /dev/null +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/osm/OpenStreetMapWorker.java @@ -0,0 +1,178 @@ +package com.mendhak.gpslogger.senders.osm; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.work.Worker; +import androidx.work.WorkerParameters; + +import com.mendhak.gpslogger.common.AppSettings; +import com.mendhak.gpslogger.common.PreferenceHelper; +import com.mendhak.gpslogger.common.Strings; +import com.mendhak.gpslogger.common.events.UploadEvents; +import com.mendhak.gpslogger.common.network.Networks; +import com.mendhak.gpslogger.common.slf4j.Logs; + +import net.openid.appauth.AuthState; +import net.openid.appauth.AuthorizationException; +import net.openid.appauth.AuthorizationService; + +import org.slf4j.Logger; + +import java.io.File; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.net.ssl.X509TrustManager; + +import de.greenrobot.event.EventBus; +import okhttp3.MediaType; +import okhttp3.MultipartBody; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; + +public class OpenStreetMapWorker extends Worker { + + private static final Logger LOG = Logs.of(OpenStreetMapWorker.class); + + private String openStreetMapAccessToken; + + public OpenStreetMapWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { + super(context, workerParams); + } + + @NonNull + @Override + public Result doWork() { + + String filePath = getInputData().getString("filePath"); + File fileToUpload = new File(filePath); + + final AtomicBoolean taskDone = new AtomicBoolean(false); + AuthState authState = OpenStreetMapManager.getAuthState(); + boolean success; + String failureMessage = ""; + Throwable failureThrowable = null; + + if(!authState.isAuthorized()){ + EventBus.getDefault().post(new UploadEvents.OpenStreetMap().failed("Could not upload to OpenStreetMap. Not authorized.")); + return Result.failure(); + } + + try { + AuthorizationService authorizationService = OpenStreetMapManager.getAuthorizationService(AppSettings.getInstance()); + + // The performActionWithFreshTokens seems to happen on a UI thread! (Why??) + // So I can't do network calls on this thread. + // Instead, updating a class level variable, and waiting for it afterwards. + // https://github.com/openid/AppAuth-Android/issues/123 + authState.performActionWithFreshTokens(authorizationService, new AuthState.AuthStateAction() { + @Override + public void execute(@Nullable String accessToken, @Nullable String idToken, @Nullable AuthorizationException ex) { + if (ex != null){ +// EventBus.getDefault().post(new UploadEvents.OpenStreetMap().failed(ex.toJsonString(), ex)); + taskDone.set(true); + LOG.error(ex.toJsonString(), ex); + return; + } + + openStreetMapAccessToken = accessToken; + taskDone.set(true); + } + }); + + // Wait for the performActionWithFreshTokens.execute callback + // (which happens on the UI thread for some reason) to complete. + while (!taskDone.get()) { + Thread.sleep(500); + } + + if (Strings.isNullOrEmpty(openStreetMapAccessToken)) { + LOG.error("Failed to fetch Access Token for OpenStreetMap. Stopping this job."); + success = false; + failureMessage = "Failed to fetch Access Token for OpenStreetMap."; +// EventBus.getDefault().post(new UploadEvents.OpenStreetMap().failed("Failed to fetch Access Token for OpenStreetMap.")); +// return Result.failure(); + } + else { + + PreferenceHelper preferenceHelper = PreferenceHelper.getInstance(); + String description = preferenceHelper.getOSMDescription(); + String tags = preferenceHelper.getOSMTags(); + String visibility = preferenceHelper.getOSMVisibility(); + + + OkHttpClient client = new OkHttpClient.Builder() + .sslSocketFactory(Networks.getSocketFactory(AppSettings.getInstance()), + (X509TrustManager) Networks.getTrustManager(AppSettings.getInstance())) + .build(); + + RequestBody requestBody = new MultipartBody.Builder() + .setType(MultipartBody.FORM) + .addFormDataPart("file", fileToUpload.getName(), RequestBody.create(MediaType.parse("application/xml+gpx"), fileToUpload)) + .addFormDataPart("description", Strings.isNullOrEmpty(description) ? "GPSLogger for Android" : description) + .addFormDataPart("tags", tags) + .addFormDataPart("visibility",visibility) + .build(); + + Request request = new Request.Builder() + .url("https://www.openstreetmap.org/api/0.6/gpx/create") + .addHeader("Authorization", "Bearer " + openStreetMapAccessToken) + .post(requestBody) + .build(); + + Response response = client.newCall(request).execute(); + ResponseBody body = response.body(); + + if(response.isSuccessful()){ + String message = body.string(); + LOG.debug("Response from OpenStreetMap: " + message); + LOG.info("OpenStreetMap - file uploaded"); + success = true; + } + else { + failureMessage = "Failed to upload to OpenStreetMap"; + if(body != null){ + failureMessage = body.string(); + } +// body.close(); + EventBus.getDefault().post(new UploadEvents.OpenStreetMap().failed()); + success = false; + + } + } + } + catch(Exception ex){ + success = false; + failureMessage = ex.getMessage(); + failureThrowable = ex; + } + + + if(success){ + EventBus.getDefault().post(new UploadEvents.OpenStreetMap().succeeded()); + return Result.success(); + } + else { + if(getRunAttemptCount() < getRetryLimit()){ + LOG.warn(String.format("OpenStreetMap Upload - attempt %d of %d failed, will retry", getRunAttemptCount(), getRetryLimit())); + return Result.retry(); + } + + if(failureThrowable == null){ + failureThrowable = new Exception(failureMessage); + } + + EventBus.getDefault().post(new UploadEvents.OpenStreetMap().failed(failureMessage, failureThrowable)); + return Result.failure(); + + } + } + + protected int getRetryLimit() { + return 3; + } +} From 4ada1b74781293ebedb6c6dac44bfca216fdd108 Mon Sep 17 00:00:00 2001 From: mendhak Date: Sun, 17 Mar 2024 15:21:12 +0000 Subject: [PATCH 31/73] Remove unused OpenStreetMap job --- .../mendhak/gpslogger/senders/osm/OSMJob.java | 178 ------------------ 1 file changed, 178 deletions(-) delete mode 100644 gpslogger/src/main/java/com/mendhak/gpslogger/senders/osm/OSMJob.java diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/osm/OSMJob.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/osm/OSMJob.java deleted file mode 100644 index bb9c5116f..000000000 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/osm/OSMJob.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (C) 2016 mendhak - * - * This file is part of GPSLogger for Android. - * - * GPSLogger for Android is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * GPSLogger for Android is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GPSLogger for Android. If not, see . - */ - -package com.mendhak.gpslogger.senders.osm; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.birbit.android.jobqueue.Job; -import com.birbit.android.jobqueue.Params; -import com.birbit.android.jobqueue.RetryConstraint; -import com.mendhak.gpslogger.common.AppSettings; -import com.mendhak.gpslogger.common.Strings; -import com.mendhak.gpslogger.common.events.UploadEvents; -import com.mendhak.gpslogger.common.network.Networks; -import com.mendhak.gpslogger.common.slf4j.Logs; - -import net.openid.appauth.AuthState; -import net.openid.appauth.AuthorizationException; -import net.openid.appauth.AuthorizationService; - -import org.slf4j.Logger; - -import java.io.File; -import java.util.concurrent.atomic.AtomicBoolean; - -import javax.net.ssl.X509TrustManager; - -import de.greenrobot.event.EventBus; -import okhttp3.MediaType; -import okhttp3.MultipartBody; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; -import okhttp3.ResponseBody; - -public class OSMJob extends Job { - - - private static final Logger LOG = Logs.of(OSMJob.class); - - private final AtomicBoolean taskDone = new AtomicBoolean(false); - private String openStreetMapAccessToken; - String gpsTraceUrl; - File chosenFile; - String description; - String tags; - String visibility; - - protected OSMJob(String gpsTraceUrl, File chosenFile, String description, String tags, String visibility) { - super(new Params(1).requireNetwork().persist().addTags(getJobTag(chosenFile))); - - this.gpsTraceUrl = gpsTraceUrl; - this.chosenFile = chosenFile; - this.description = description; - this.tags = tags; - this.visibility = visibility; - } - - @Override - public void onAdded() { - - LOG.debug("OSM Job added"); - } - - @Override - public void onRun() throws Throwable { - - AuthState authState = OpenStreetMapManager.getAuthState(); - - if(!authState.isAuthorized()){ - EventBus.getDefault().post(new UploadEvents.OpenStreetMap().failed("Could not upload to OpenStreetMap. Not authorized.")); - } - - AuthorizationService authorizationService = OpenStreetMapManager.getAuthorizationService(AppSettings.getInstance()); - - // The performActionWithFreshTokens seems to happen on a UI thread! (Why??) - // So I can't do network calls on this thread. - // Instead, updating a class level variable, and waiting for it afterwards. - // https://github.com/openid/AppAuth-Android/issues/123 - authState.performActionWithFreshTokens(authorizationService, new AuthState.AuthStateAction() { - @Override - public void execute(@Nullable String accessToken, @Nullable String idToken, @Nullable AuthorizationException ex) { - if (ex != null){ - EventBus.getDefault().post(new UploadEvents.OpenStreetMap().failed(ex.toJsonString(), ex)); - taskDone.set(true); - LOG.error(ex.toJsonString(), ex); - return; - } - - openStreetMapAccessToken = accessToken; - taskDone.set(true); - } - }); - - // Wait for the performActionWithFreshTokens.execute callback - // (which happens on the UI thread for some reason) to complete. - while (!taskDone.get()) { - Thread.sleep(500); - } - - if (Strings.isNullOrEmpty(openStreetMapAccessToken)) { - LOG.error("Failed to fetch Access Token for OpenStreetMap. Stopping this job."); - return; - } - - OkHttpClient client = new OkHttpClient.Builder() - .sslSocketFactory(Networks.getSocketFactory(AppSettings.getInstance()), - (X509TrustManager) Networks.getTrustManager(AppSettings.getInstance())) - .build(); - - RequestBody requestBody = new MultipartBody.Builder() - .setType(MultipartBody.FORM) - .addFormDataPart("file", chosenFile.getName(), RequestBody.create(MediaType.parse("application/xml+gpx"), chosenFile)) - .addFormDataPart("description", Strings.isNullOrEmpty(description) ? "GPSLogger for Android" : description) - .addFormDataPart("tags", tags) - .addFormDataPart("visibility",visibility) - .build(); - - Request request = new Request.Builder() - .url(gpsTraceUrl) - .addHeader("Authorization", "Bearer " + openStreetMapAccessToken) - .post(requestBody) - .build(); - - Response response = client.newCall(request).execute(); - ResponseBody body = response.body(); - - if(response.isSuccessful()){ - String message = body.string(); - LOG.debug("Response from OpenStreetMap: " + message); - LOG.info("OpenStreetMap - file uploaded"); - EventBus.getDefault().post(new UploadEvents.OpenStreetMap().succeeded()); - } - else { - body.close(); - EventBus.getDefault().post(new UploadEvents.OpenStreetMap().failed()); - } - } - - @Override - protected void onCancel(int cancelReason, @Nullable Throwable throwable) { - LOG.error("Could not send to OpenStreetMap", throwable); - EventBus.getDefault().post(new UploadEvents.OpenStreetMap().failed("Could not send to OpenStreetMap", throwable)); - } - - @Override - protected RetryConstraint shouldReRunOnThrowable(@NonNull Throwable throwable, int runCount, int maxRunCount) { - return RetryConstraint.createExponentialBackoff(runCount, 3000); - } - - public static String getJobTag(File gpxFile) { - return "OSM" + gpxFile.getName(); - - } - - @Override - protected int getRetryLimit() { - return 3; - } -} From d1cea064baeaec5240875c80ae0a103582024bf4 Mon Sep 17 00:00:00 2001 From: mendhak Date: Sun, 17 Mar 2024 17:39:10 +0000 Subject: [PATCH 32/73] Move OwnCloud into workmanager --- .../senders/owncloud/OwnCloudManager.java | 50 +++--- .../senders/owncloud/OwnCloudWorker.java | 155 ++++++++++++++++++ .../settings/OwnCloudSettingsFragment.java | 2 +- 3 files changed, 178 insertions(+), 29 deletions(-) create mode 100644 gpslogger/src/main/java/com/mendhak/gpslogger/senders/owncloud/OwnCloudWorker.java diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/owncloud/OwnCloudManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/owncloud/OwnCloudManager.java index 0059ca7cd..114c3394a 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/owncloud/OwnCloudManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/owncloud/OwnCloudManager.java @@ -19,12 +19,13 @@ package com.mendhak.gpslogger.senders.owncloud; -import com.birbit.android.jobqueue.CancelResult; -import com.birbit.android.jobqueue.JobManager; -import com.birbit.android.jobqueue.TagConstraint; +import androidx.work.OneTimeWorkRequest; +import androidx.work.WorkManager; + import com.mendhak.gpslogger.common.AppSettings; import com.mendhak.gpslogger.common.PreferenceHelper; import com.mendhak.gpslogger.common.Strings; +import com.mendhak.gpslogger.common.Systems; import com.mendhak.gpslogger.common.events.UploadEvents; import com.mendhak.gpslogger.common.slf4j.Logs; import com.mendhak.gpslogger.loggers.Files; @@ -34,7 +35,9 @@ import org.slf4j.Logger; import java.io.File; +import java.util.HashMap; import java.util.List; +import java.util.Objects; public class OwnCloudManager extends FileSender { @@ -45,28 +48,24 @@ public OwnCloudManager(PreferenceHelper preferenceHelper) { this.preferenceHelper = preferenceHelper; } - public void testOwnCloud(final String servername, final String username, final String password, final String directory) { - - + public void testOwnCloud() { try { final File testFile = Files.createTestFile(); - final JobManager jobManager = AppSettings.getJobManager(); - jobManager.cancelJobsInBackground(new CancelResult.AsyncCancelCallback() { - @Override - public void onCancelled(CancelResult cancelResult) { - jobManager.addJobInBackground(new OwnCloudJob(servername, username, password, directory, - testFile, testFile.getName())); - } - }, TagConstraint.ANY, OwnCloudJob.getJobTag(testFile)); + + String tag = String.valueOf(Objects.hashCode(testFile)); + HashMap dataMap = new HashMap(){{ + put("filePath", testFile.getAbsolutePath()); + }}; + OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(OwnCloudWorker.class, dataMap); + WorkManager.getInstance(AppSettings.getInstance()) + .enqueueUniqueWork(tag, androidx.work.ExistingWorkPolicy.REPLACE, workRequest); } catch (Exception ex) { EventBus.getDefault().post(new UploadEvents.Ftp().failed()); LOG.error("Error while testing ownCloud upload: " + ex.getMessage()); } - - LOG.debug("Added background ownCloud upload job"); } @@ -107,19 +106,14 @@ public String getName() { public void uploadFile(final File f) { - final JobManager jobManager = AppSettings.getJobManager(); - jobManager.cancelJobsInBackground(new CancelResult.AsyncCancelCallback() { - @Override - public void onCancelled(CancelResult cancelResult) { - jobManager.addJobInBackground(new OwnCloudJob( - preferenceHelper.getOwnCloudBaseUrl(), - preferenceHelper.getOwnCloudUsername(), - preferenceHelper.getOwnCloudPassword(), - preferenceHelper.getOwnCloudDirectory(), - f, f.getName())); - } - }, TagConstraint.ANY, OwnCloudJob.getJobTag(f)); + String tag = String.valueOf(Objects.hashCode(f)); + HashMap dataMap = new HashMap(){{ + put("filePath", f.getAbsolutePath()); + }}; + OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(OwnCloudWorker.class, dataMap); + WorkManager.getInstance(AppSettings.getInstance()) + .enqueueUniqueWork(tag, androidx.work.ExistingWorkPolicy.REPLACE, workRequest); } @Override diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/owncloud/OwnCloudWorker.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/owncloud/OwnCloudWorker.java new file mode 100644 index 000000000..d4825f3cc --- /dev/null +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/owncloud/OwnCloudWorker.java @@ -0,0 +1,155 @@ +package com.mendhak.gpslogger.senders.owncloud; + +import android.content.Context; +import android.net.Uri; + +import androidx.annotation.NonNull; +import androidx.work.Worker; +import androidx.work.WorkerParameters; + +import com.mendhak.gpslogger.common.AppSettings; +import com.mendhak.gpslogger.common.PreferenceHelper; +import com.mendhak.gpslogger.common.Strings; +import com.mendhak.gpslogger.common.events.UploadEvents; +import com.mendhak.gpslogger.common.network.LocalX509TrustManager; +import com.mendhak.gpslogger.common.network.Networks; +import com.mendhak.gpslogger.common.slf4j.Logs; +import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.OwnCloudClientFactory; +import com.owncloud.android.lib.common.OwnCloudCredentialsFactory; +import com.owncloud.android.lib.common.network.AdvancedSslSocketFactory; +import com.owncloud.android.lib.common.network.AdvancedX509TrustManager; +import com.owncloud.android.lib.common.operations.OnRemoteOperationListener; +import com.owncloud.android.lib.common.operations.RemoteOperation; +import com.owncloud.android.lib.common.operations.RemoteOperationResult; +import com.owncloud.android.lib.resources.files.CreateRemoteFolderOperation; +import com.owncloud.android.lib.resources.files.FileUtils; +import com.owncloud.android.lib.resources.files.UploadRemoteFileOperation; + +import org.apache.commons.httpclient.protocol.Protocol; +import org.apache.commons.httpclient.protocol.ProtocolSocketFactory; +import org.slf4j.Logger; + +import java.io.File; +import java.security.GeneralSecurityException; +import java.util.concurrent.atomic.AtomicInteger; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; + +import de.greenrobot.event.EventBus; + +public class OwnCloudWorker extends Worker implements OnRemoteOperationListener { + + private static final Logger LOG = Logs.of(OwnCloudWorker.class); + + final AtomicInteger taskStatus = new AtomicInteger(-1); + Throwable failureThrowable = null; + String failureMessage = ""; + + public OwnCloudWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { + super(context, workerParams); + } + + @NonNull + @Override + public Result doWork() { + + String filePath = getInputData().getString("filePath"); + if(Strings.isNullOrEmpty(filePath)) { + LOG.error("No file path provided to upload to OwnCloud"); + return Result.failure(); + } + File fileToUpload = new File(filePath); + + PreferenceHelper preferenceHelper = PreferenceHelper.getInstance(); + String servername = preferenceHelper.getOwnCloudBaseUrl(); + String username = preferenceHelper.getOwnCloudUsername(); + String password = preferenceHelper.getOwnCloudPassword(); + String directory = preferenceHelper.getOwnCloudDirectory(); + + + try { + LOG.debug("ownCloud Job: Uploading '" + fileToUpload.getName() + "'"); + + + Protocol pr = Protocol.getProtocol("https"); + + try { + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init( + null, + new TrustManager[] { new LocalX509TrustManager(Networks.getKnownServersStore(AppSettings.getInstance())) }, + null + ); + + ProtocolSocketFactory psf = new AdvancedSslSocketFactory(sslContext, new AdvancedX509TrustManager(Networks.getKnownServersStore(AppSettings.getInstance())), null); + + + Protocol.registerProtocol( "https", new Protocol("https", psf, 443)); + + } catch (GeneralSecurityException e) { + LOG.error("Self-signed confident SSL context could not be loaded", e); + } + + OwnCloudClient client = OwnCloudClientFactory.createOwnCloudClient(Uri.parse(servername), AppSettings.getInstance(), true); + client.setDefaultTimeouts('\uea60', '\uea60'); + client.setFollowRedirects(true); + client.setCredentials( + OwnCloudCredentialsFactory.newBasicCredentials(username, password) + ); + + //Create the folder, in case it doesn't already exist on OwnCloud. + CreateRemoteFolderOperation createOperation = new CreateRemoteFolderOperation(directory, false); + createOperation.execute( client); + + String remotePath = directory + FileUtils.PATH_SEPARATOR + fileToUpload.getName(); + String mimeType = "application/octet-stream"; //unused + UploadRemoteFileOperation uploadOperation = new UploadRemoteFileOperation(fileToUpload.getAbsolutePath(), remotePath, mimeType); + uploadOperation.execute(client,this,null); + + /* + Doing this simply because ListenerWorker is too complicated to implement, + And the documentation is not clear on how to use it. + */ + for(int i = 0; i < 24; i++) { + Thread.sleep(5000); + if(taskStatus.get() != -1) { + break; + } + } + + // If it failed, OR never completed in time. + if(taskStatus.get() <= 0) { + LOG.error("Failed to upload to OwnCloud"); + EventBus.getDefault().post(new UploadEvents.OwnCloud().failed(failureMessage, failureThrowable)); + return Result.failure(); + } + else { + LOG.info("OwnCloud - file uploaded"); + EventBus.getDefault().post(new UploadEvents.OwnCloud().succeeded()); + return Result.success(); + } + } + catch (Exception ex) { + LOG.error("Error in OwnCloudWorker.doWork(): " + ex.getMessage()); + EventBus.getDefault().post(new UploadEvents.OwnCloud().failed(ex.getMessage(), ex)); + return Result.failure(); + } + } + + @Override + public void onRemoteOperationFinish(RemoteOperation remoteOperation, RemoteOperationResult result) { + + if (!result.isSuccess()) { + LOG.error(result.getLogMessage(), result.getException()); + failureThrowable = result.getException(); + failureMessage = result.getLogMessage(); + taskStatus.set(0); + } else { + taskStatus.set(1); + } + + LOG.debug("ownCloud Job: onRun finished"); + } +} diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/OwnCloudSettingsFragment.java b/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/OwnCloudSettingsFragment.java index d1defa51f..08f500626 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/OwnCloudSettingsFragment.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/OwnCloudSettingsFragment.java @@ -195,7 +195,7 @@ else if(preference.getKey().equals("owncloud_test")){ Dialogs.progress((FragmentActivity) getActivity(), getString(R.string.owncloud_testing)); OwnCloudManager helper = new OwnCloudManager(PreferenceHelper.getInstance()); - helper.testOwnCloud(server, user, pass, directory); + helper.testOwnCloud(); } From 821fd0b26edd193340763aa42207f5ee2880f2ef Mon Sep 17 00:00:00 2001 From: mendhak Date: Sun, 17 Mar 2024 18:13:38 +0000 Subject: [PATCH 33/73] Move SFTP upload to WorkManager --- .../gpslogger/senders/sftp/SFTPManager.java | 21 ++- .../gpslogger/senders/sftp/SFTPWorker.java | 173 ++++++++++++++++++ 2 files changed, 186 insertions(+), 8 deletions(-) create mode 100644 gpslogger/src/main/java/com/mendhak/gpslogger/senders/sftp/SFTPWorker.java diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/sftp/SFTPManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/sftp/SFTPManager.java index a072500ea..ec6fb68e0 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/sftp/SFTPManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/sftp/SFTPManager.java @@ -1,16 +1,22 @@ package com.mendhak.gpslogger.senders.sftp; +import androidx.work.OneTimeWorkRequest; +import androidx.work.WorkManager; + import com.birbit.android.jobqueue.CancelResult; import com.birbit.android.jobqueue.JobManager; import com.birbit.android.jobqueue.TagConstraint; import com.mendhak.gpslogger.common.AppSettings; import com.mendhak.gpslogger.common.PreferenceHelper; import com.mendhak.gpslogger.common.Strings; +import com.mendhak.gpslogger.common.Systems; import com.mendhak.gpslogger.senders.FileSender; import java.io.File; +import java.util.HashMap; import java.util.List; +import java.util.Objects; public class SFTPManager extends FileSender { @@ -28,14 +34,13 @@ public void uploadFile(List files) { } public void uploadFile(final File file){ - final JobManager jobManager = AppSettings.getJobManager(); - jobManager.cancelJobsInBackground(new CancelResult.AsyncCancelCallback() { - @Override - public void onCancelled(CancelResult cancelResult) { - jobManager.addJobInBackground(new SFTPJob(file, preferenceHelper.getSFTPRemoteServerPath(), preferenceHelper.getSFTPHost(),preferenceHelper.getSFTPPort(),preferenceHelper.getSFTPPrivateKeyFilePath(), - preferenceHelper.getSFTPPrivateKeyPassphrase(),preferenceHelper.getSFTPUser(),preferenceHelper.getSFTPPassword(),preferenceHelper.getSFTPKnownHostKey())); - } - }, TagConstraint.ANY, SFTPJob.getJobTag(file)); + String tag = String.valueOf(Objects.hashCode(file)); + HashMap dataMap = new HashMap(){{ + put("filePath", file.getAbsolutePath()); + }}; + OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(SFTPWorker.class, dataMap); + WorkManager.getInstance(AppSettings.getInstance()) + .enqueueUniqueWork(tag, androidx.work.ExistingWorkPolicy.REPLACE, workRequest); } @Override diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/sftp/SFTPWorker.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/sftp/SFTPWorker.java new file mode 100644 index 000000000..2438a78a3 --- /dev/null +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/sftp/SFTPWorker.java @@ -0,0 +1,173 @@ +package com.mendhak.gpslogger.senders.sftp; + +import android.content.Context; +import android.util.Base64; + +import androidx.annotation.NonNull; +import androidx.work.Worker; +import androidx.work.WorkerParameters; + +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.HostKey; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.SftpException; +import com.mendhak.gpslogger.common.PreferenceHelper; +import com.mendhak.gpslogger.common.Strings; +import com.mendhak.gpslogger.common.events.UploadEvents; +import com.mendhak.gpslogger.common.slf4j.Logs; + + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.slf4j.Logger; + +import java.io.File; +import java.io.FileInputStream; +import java.security.Security; +import java.util.Properties; + +import de.greenrobot.event.EventBus; + +public class SFTPWorker extends Worker { + + private static final Logger LOG = Logs.of(SFTPWorker.class); + + public SFTPWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { + + super(context, workerParams); + + try { + Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); + Security.insertProviderAt(new BouncyCastleProvider(), 1); + } + catch(Exception ex){ + LOG.error("Could not add BouncyCastle provider.", ex); + } + } + + @NonNull + @Override + public Result doWork() { + + String filePath = getInputData().getString("filePath"); + if(Strings.isNullOrEmpty(filePath)) { + LOG.error("No file path provided to upload to SFTP"); + EventBus.getDefault().post(new UploadEvents.SFTP().failed("No file path provided to upload to SFTP", null)); + return Result.failure(); + } + File fileToUpload = new File(filePath); + + PreferenceHelper preferenceHelper = PreferenceHelper.getInstance(); + String hostKey = preferenceHelper.getSFTPKnownHostKey(); + String host = preferenceHelper.getSFTPHost(); + String username = preferenceHelper.getSFTPUser(); + String password = preferenceHelper.getSFTPPassword(); + int port = preferenceHelper.getSFTPPort(); + String remoteDir = preferenceHelper.getSFTPRemoteServerPath(); + String pathToPrivateKey = preferenceHelper.getSFTPPrivateKeyFilePath(); + String privateKeyPassphrase = preferenceHelper.getSFTPPrivateKeyPassphrase(); + + LOG.debug("SFTP Job onRun"); + com.jcraft.jsch.Session session = null; + JSch.setLogger(new SftpLogger()); + final JSch jsch = new JSch(); + FileInputStream fis = null; + + try { + String keystring = hostKey; + + if (!Strings.isNullOrEmpty(keystring)) { + byte[] key = Base64.decode(keystring, Base64.DEFAULT); + jsch.getHostKeyRepository().add(new HostKey(host, key), null); + } + + if(!Strings.isNullOrEmpty(pathToPrivateKey)){ + jsch.addIdentity(pathToPrivateKey, privateKeyPassphrase); + } + + session = jsch.getSession(username, host, port); + session.setPassword(password); + + Properties prop = new Properties(); + prop.put("StrictHostKeyChecking", "yes"); + session.setConfig(prop); + + LOG.debug("Connecting..."); + session.connect(); + + if (session.isConnected()) { + + LOG.debug("Connected, opening SFTP channel"); + Channel channel = session.openChannel("sftp"); + channel.connect(); + ChannelSftp channelSftp = (ChannelSftp) channel; + LOG.debug("Changing directory to " + remoteDir); + channelSftp.cd(remoteDir); + LOG.debug("Uploading " + fileToUpload.getName() + " to remote server"); + channelSftp.put(new FileInputStream(fileToUpload), fileToUpload.getName(), ChannelSftp.OVERWRITE); + + LOG.debug("Disconnecting"); + channelSftp.disconnect(); + channel.disconnect(); + session.disconnect(); + + LOG.info("SFTP - file uploaded"); + EventBus.getDefault().post(new UploadEvents.SFTP().succeeded()); + return Result.success(); + } else { + EventBus.getDefault().post(new UploadEvents.SFTP().failed("Could not connect, unknown reasons", null)); + return Result.failure(); + } + + } catch (SftpException sftpex) { + LOG.error(sftpex.getMessage(), sftpex); + EventBus.getDefault().post(new UploadEvents.SFTP().failed(sftpex.getMessage(), sftpex)); + return Result.failure(); + } catch (final JSchException jex) { + LOG.error(jex.getMessage(), jex); + + if (jex.getMessage().contains("reject HostKey") || jex.getMessage().contains("HostKey has been changed")) { + LOG.debug(session.getHostKey().getKey()); + UploadEvents.SFTP sftpException = new UploadEvents.SFTP(); + sftpException.hostKey = session.getHostKey().getKey(); + sftpException.fingerprint = session.getHostKey().getFingerPrint(jsch); + EventBus.getDefault().post(sftpException.failed(jex.getMessage(), jex)); + } else { + LOG.error(jex.getMessage(), jex); + EventBus.getDefault().post(new UploadEvents.SFTP().failed(jex.getMessage(), jex)); + } + + return Result.failure(); + } catch (Exception ex) { + LOG.error(ex.getMessage(), ex); + EventBus.getDefault().post(new UploadEvents.SFTP().failed(ex.getMessage(), ex)); + return Result.failure(); + } finally { + try { + fis.close(); + } catch (Exception ee) { + } + } + } + + public static class SftpLogger implements com.jcraft.jsch.Logger { + + public boolean isEnabled(int level){ + return true; + } + public void log(int level, String message){ + switch(level){ + case FATAL: + case ERROR: + LOG.error(message); + break; + case WARN: + case INFO: + case DEBUG: + LOG.debug(message); + break; + } + } + } +} From 4e8a83bb16bf77763acff39364b2730d59fa3d6a Mon Sep 17 00:00:00 2001 From: mendhak Date: Sun, 17 Mar 2024 18:14:00 +0000 Subject: [PATCH 34/73] Remove unused Owncloud and unused sftp job queue --- .../senders/owncloud/OwnCloudJob.java | 161 ---------------- .../gpslogger/senders/sftp/SFTPJob.java | 179 ------------------ 2 files changed, 340 deletions(-) delete mode 100644 gpslogger/src/main/java/com/mendhak/gpslogger/senders/owncloud/OwnCloudJob.java delete mode 100644 gpslogger/src/main/java/com/mendhak/gpslogger/senders/sftp/SFTPJob.java diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/owncloud/OwnCloudJob.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/owncloud/OwnCloudJob.java deleted file mode 100644 index 29a12b1b1..000000000 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/owncloud/OwnCloudJob.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (C) 2016 mendhak - * - * This file is part of GPSLogger for Android. - * - * GPSLogger for Android is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * GPSLogger for Android is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GPSLogger for Android. If not, see . - */ - -package com.mendhak.gpslogger.senders.owncloud; - -import android.net.Uri; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.birbit.android.jobqueue.Job; -import com.birbit.android.jobqueue.Params; -import com.birbit.android.jobqueue.RetryConstraint; -import com.mendhak.gpslogger.common.AppSettings; -import com.mendhak.gpslogger.common.events.UploadEvents; -import com.mendhak.gpslogger.common.network.LocalX509TrustManager; -import com.mendhak.gpslogger.common.network.Networks; -import com.mendhak.gpslogger.common.slf4j.Logs; -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.OwnCloudClientFactory; -import com.owncloud.android.lib.common.OwnCloudCredentialsFactory; -import com.owncloud.android.lib.common.network.AdvancedSslSocketFactory; -import com.owncloud.android.lib.common.network.AdvancedX509TrustManager; -import com.owncloud.android.lib.common.operations.OnRemoteOperationListener; -import com.owncloud.android.lib.common.operations.RemoteOperation; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.resources.files.CreateRemoteFolderOperation; -import com.owncloud.android.lib.resources.files.FileUtils; -import com.owncloud.android.lib.resources.files.UploadRemoteFileOperation; - -import org.apache.commons.httpclient.protocol.Protocol; -import org.apache.commons.httpclient.protocol.ProtocolSocketFactory; -import org.slf4j.Logger; - -import java.io.File; -import java.security.GeneralSecurityException; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; - -import de.greenrobot.event.EventBus; - -public class OwnCloudJob extends Job implements OnRemoteOperationListener { - - private static final Logger LOG = Logs.of(OwnCloudJob.class); - - - String servername; - String username; - String password; - String directory; - File localFile; - String remoteFileName; - - public OwnCloudJob(String servername, String username, String password, String directory, - File localFile, String remoteFileName) - { - super(new Params(1).requireNetwork().persist().addTags(getJobTag(localFile))); - this.servername = servername; - this.username = username; - this.password = password; - this.directory = directory; - this.localFile = localFile; - this.remoteFileName = remoteFileName; - - } - - @Override - public void onAdded() { - LOG.debug("ownCloud Job: onAdded"); - } - - @Override - public void onRun() throws Throwable { - - LOG.debug("ownCloud Job: Uploading '" + localFile.getName() + "'"); - - - Protocol pr = Protocol.getProtocol("https"); - - try { - SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init( - null, - new TrustManager[] { new LocalX509TrustManager(Networks.getKnownServersStore(AppSettings.getInstance())) }, - null - ); - - ProtocolSocketFactory psf = new AdvancedSslSocketFactory(sslContext, new AdvancedX509TrustManager(Networks.getKnownServersStore(AppSettings.getInstance())), null); - - - Protocol.registerProtocol( "https", new Protocol("https", psf, 443)); - - } catch (GeneralSecurityException e) { - LOG.error("Self-signed confident SSL context could not be loaded", e); - } - - - OwnCloudClient client = OwnCloudClientFactory.createOwnCloudClient(Uri.parse(servername), AppSettings.getInstance(), true); - client.setDefaultTimeouts('\uea60', '\uea60'); - client.setFollowRedirects(true); - client.setCredentials( - OwnCloudCredentialsFactory.newBasicCredentials(username, password) - ); - - //Create the folder, in case it doesn't already exist on OwnCloud. - CreateRemoteFolderOperation createOperation = new CreateRemoteFolderOperation(directory, false); - createOperation.execute( client); - - String remotePath = directory + FileUtils.PATH_SEPARATOR + localFile.getName(); - String mimeType = "application/octet-stream"; //unused - UploadRemoteFileOperation uploadOperation = new UploadRemoteFileOperation(localFile.getAbsolutePath(), remotePath, mimeType); - uploadOperation.execute(client,this,null); - } - - @Override - protected void onCancel(int cancelReason, @Nullable Throwable throwable) { - LOG.debug("ownCloud Job: onCancel"); - } - - @Override - protected RetryConstraint shouldReRunOnThrowable(@NonNull Throwable throwable, int runCount, int maxRunCount) { - LOG.error("Could not upload to OwnCloud", throwable); - EventBus.getDefault().post(new UploadEvents.OwnCloud().failed("Could not upload to OwnCloud", throwable)); - return RetryConstraint.CANCEL; - } - - @Override - public void onRemoteOperationFinish(RemoteOperation remoteOperation, RemoteOperationResult result) { - - if (!result.isSuccess()) { - LOG.error(result.getLogMessage(), result.getException()); - EventBus.getDefault().post(new UploadEvents.OwnCloud().failed(result.getLogMessage(), result.getException())); - } else { - LOG.info("OwnCloud - file uploaded"); - EventBus.getDefault().post(new UploadEvents.OwnCloud().succeeded()); - } - - LOG.debug("ownCloud Job: onRun finished"); - } - - public static String getJobTag(File gpxFile) { - return "OWNCLOUD" + gpxFile.getName(); - } -} \ No newline at end of file diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/sftp/SFTPJob.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/sftp/SFTPJob.java deleted file mode 100644 index 03855efcc..000000000 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/sftp/SFTPJob.java +++ /dev/null @@ -1,179 +0,0 @@ -package com.mendhak.gpslogger.senders.sftp; - - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import android.util.Base64; - -import com.birbit.android.jobqueue.Job; -import com.birbit.android.jobqueue.Params; -import com.birbit.android.jobqueue.RetryConstraint; -import com.jcraft.jsch.*; -import com.mendhak.gpslogger.common.Strings; -import com.mendhak.gpslogger.common.events.UploadEvents; -import com.mendhak.gpslogger.common.slf4j.Logs; -import de.greenrobot.event.EventBus; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.slf4j.Logger; -import java.io.*; -import java.security.Security; -import java.util.Properties; - -public class SFTPJob extends Job { - private static final Logger LOG = Logs.of(SFTPJob.class); - private final File localFile; - private final String host; - private final int port; - private final String pathToPrivateKey; - private final String privateKeyPassphrase; - private final String username; - private final String password; - private final String hostKey; - private final String remoteDir; - - public SFTPJob(File localFile, String remoteDir, String host, int port, String pathToPrivateKey, String privateKeyPassphrase, String username, String password, String hostKey) { - super(new Params(1).requireNetwork().persist().addTags(getJobTag(localFile))); - - try { - Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); - Security.insertProviderAt(new BouncyCastleProvider(), 1); - } - catch(Exception ex){ - LOG.error("Could not add BouncyCastle provider.", ex); - } - - this.localFile = localFile; - this.remoteDir = remoteDir; - this.host = host; - this.port = port; - this.pathToPrivateKey = pathToPrivateKey; - this.privateKeyPassphrase = privateKeyPassphrase; - this.username = username; - this.password = password; - this.hostKey = hostKey; - } - - - @Override - public void onAdded() { - LOG.debug("SFTP Job added"); - } - - @Override - public void onRun() throws Throwable { - LOG.debug("SFTP Job onRun"); - com.jcraft.jsch.Session session = null; - JSch.setLogger(new SftpLogger()); - final JSch jsch = new JSch(); - FileInputStream fis = null; - - try { - String keystring = this.hostKey; - - if (!Strings.isNullOrEmpty(keystring)) { - byte[] key = Base64.decode(keystring, Base64.DEFAULT); - jsch.getHostKeyRepository().add(new HostKey(host, key), null); - } - - if(!Strings.isNullOrEmpty(this.pathToPrivateKey)){ - jsch.addIdentity(this.pathToPrivateKey, this.privateKeyPassphrase); - } - - session = jsch.getSession(this.username, this.host, this.port); - session.setPassword(this.password); - - Properties prop = new Properties(); - prop.put("StrictHostKeyChecking", "yes"); - session.setConfig(prop); - - LOG.debug("Connecting..."); - session.connect(); - - if (session.isConnected()) { - - LOG.debug("Connected, opening SFTP channel"); - Channel channel = session.openChannel("sftp"); - channel.connect(); - ChannelSftp channelSftp = (ChannelSftp) channel; - LOG.debug("Changing directory to " + this.remoteDir); - channelSftp.cd(this.remoteDir); - LOG.debug("Uploading " + this.localFile.getName() + " to remote server"); - channelSftp.put(new FileInputStream(this.localFile), this.localFile.getName(), ChannelSftp.OVERWRITE); - - LOG.debug("Disconnecting"); - channelSftp.disconnect(); - channel.disconnect(); - session.disconnect(); - - LOG.info("SFTP - file uploaded"); - EventBus.getDefault().post(new UploadEvents.SFTP().succeeded()); - } else { - EventBus.getDefault().post(new UploadEvents.SFTP().failed("Could not connect, unknown reasons", null)); - } - - } catch (SftpException sftpex) { - LOG.error(sftpex.getMessage(), sftpex); - EventBus.getDefault().post(new UploadEvents.SFTP().failed(sftpex.getMessage(), sftpex)); - } catch (final JSchException jex) { - LOG.error(jex.getMessage(), jex); - if (jex.getMessage().contains("reject HostKey") || jex.getMessage().contains("HostKey has been changed")) { - LOG.debug(session.getHostKey().getKey()); - UploadEvents.SFTP sftpException = new UploadEvents.SFTP(); - sftpException.hostKey = session.getHostKey().getKey(); - sftpException.fingerprint = session.getHostKey().getFingerPrint(jsch); - EventBus.getDefault().post(sftpException.failed(jex.getMessage(), jex)); - } else { - throw jex; - } - } catch (Exception ex) { - LOG.error(ex.getMessage(), ex); - EventBus.getDefault().post(new UploadEvents.SFTP().failed(ex.getMessage(), ex)); - } finally { - try { - fis.close(); - } catch (Exception ee) { - } - } - } - - @Override - protected void onCancel(int cancelReason, @Nullable Throwable throwable) { - LOG.debug("SFTP Job Cancelled"); - } - - @Override - protected RetryConstraint shouldReRunOnThrowable(@NonNull Throwable throwable, int runCount, int maxRunCount) { - LOG.error("Could not upload to SFTP server", throwable); - EventBus.getDefault().post(new UploadEvents.SFTP().failed(throwable.getMessage(), throwable)); - return RetryConstraint.CANCEL; - } - - - public static String getJobTag(File gpxFile) { - return "SFTP" + gpxFile.getName(); - } - - - public static class SftpLogger implements com.jcraft.jsch.Logger { - - public boolean isEnabled(int level){ - return true; - } - public void log(int level, String message){ - switch(level){ - case FATAL: - case ERROR: - LOG.error(message); - break; - case WARN: - case INFO: - case DEBUG: - LOG.debug(message); - break; - } - } - } - -} - - From 43a90689e9ad1e5823b7e9b215be4a53f53a1e4f Mon Sep 17 00:00:00 2001 From: mendhak Date: Sun, 17 Mar 2024 18:18:31 +0000 Subject: [PATCH 35/73] Remove references to Priority JobQueue, switched over to WorkManager --- gpslogger/build.gradle | 7 +- .../mendhak/gpslogger/common/AppSettings.java | 54 +------ .../gpslogger/common/WifiNetworkUtil.java | 135 ------------------ .../gpslogger/senders/sftp/SFTPManager.java | 3 - 4 files changed, 4 insertions(+), 195 deletions(-) delete mode 100644 gpslogger/src/main/java/com/mendhak/gpslogger/common/WifiNetworkUtil.java diff --git a/gpslogger/build.gradle b/gpslogger/build.gradle index 86d63c623..8f5f7cbb5 100644 --- a/gpslogger/build.gradle +++ b/gpslogger/build.gradle @@ -146,14 +146,9 @@ dependencies { //Progress button implementation 'com.github.dmytrodanylyk.android-process-button:library:1.0.4' - //Android Priority Jobqueue - implementation ('com.birbit:android-priority-jobqueue:2.0.1'){ - exclude group: 'com.google.android', module: 'android' - } - //Android's WorkManager implementation 'androidx.work:work-runtime:2.9.0' - // It needs Gson + // We need to use Gson to help with WorkManager limitations implementation 'com.google.code.gson:gson:2.10.1' //Event bus diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/common/AppSettings.java b/gpslogger/src/main/java/com/mendhak/gpslogger/common/AppSettings.java index 2b07d3074..b3ebd6266 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/common/AppSettings.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/common/AppSettings.java @@ -21,9 +21,7 @@ import android.app.Application; -import com.birbit.android.jobqueue.JobManager; -import com.birbit.android.jobqueue.config.Configuration; -import com.birbit.android.jobqueue.log.CustomLogger; + import com.mendhak.gpslogger.BuildConfig; import com.mendhak.gpslogger.common.slf4j.Logs; import de.greenrobot.event.EventBus; @@ -31,7 +29,7 @@ public class AppSettings extends Application { - private static JobManager jobManager; + private static AppSettings instance; private static Logger LOG; @@ -50,25 +48,10 @@ public void onCreate() { EventBus.builder().logNoSubscriberMessages(false).sendNoSubscriberEvent(false).installDefaultEventBus(); LOG.debug("EventBus configured"); - //Configure the Job Queue - Configuration config = new Configuration.Builder(getInstance()) - .networkUtil(new WifiNetworkUtil(getInstance())) - .consumerKeepAlive(60) - .minConsumerCount(0) - .maxConsumerCount(1) -// .customLogger(jobQueueLogger) - .build(); - jobManager = new JobManager(config); - LOG.debug("Job Queue configured"); - } - /** - * Returns a configured Job Queue Manager - */ - public static JobManager getJobManager() { - return jobManager; } + public AppSettings() { instance = this; } @@ -81,35 +64,4 @@ public static AppSettings getInstance() { } - private final CustomLogger jobQueueLogger = new CustomLogger() { - @Override - public boolean isDebugEnabled() { - return BuildConfig.DEBUG; - } - - @Override - public void d(String text, Object... args) { - - LOG.debug(String.format(text, args)); - } - - @Override - public void e(Throwable t, String text, Object... args) { - LOG.error(String.format(text, args), t); - } - - @Override - public void e(String text, Object... args) { - - LOG.error(String.format(text, args)); - } - - @Override - public void v(String text, Object... args) { - LOG.debug(String.format(text,args)); - } - }; - - - } diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/common/WifiNetworkUtil.java b/gpslogger/src/main/java/com/mendhak/gpslogger/common/WifiNetworkUtil.java deleted file mode 100644 index cc2c2b1d9..000000000 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/common/WifiNetworkUtil.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (C) 2016 mendhak - * - * This file is part of GPSLogger for Android. - * - * GPSLogger for Android is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * GPSLogger for Android is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GPSLogger for Android. If not, see . - */ - -package com.mendhak.gpslogger.common; - - -import android.annotation.TargetApi; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.net.ConnectivityManager; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.NetworkInfo; -import android.net.NetworkRequest; -import android.os.Build; -import android.os.PowerManager; - -import com.birbit.android.jobqueue.network.NetworkEventProvider; -import com.birbit.android.jobqueue.network.NetworkUtil; - -/** - * default implementation for network Utility to observe network events - */ -public class WifiNetworkUtil implements NetworkUtil, NetworkEventProvider { - private Listener listener; - public WifiNetworkUtil(Context context) { - context = context.getApplicationContext(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - listenForIdle(context); - } - listenNetworkViaConnectivityManager(context); - } else { - context.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - dispatchNetworkChange(context); - } - }, getNetworkIntentFilter()); - } - } - - @TargetApi(23) - private void listenNetworkViaConnectivityManager(final Context context) { - ConnectivityManager cm = (ConnectivityManager) context - .getSystemService(Context.CONNECTIVITY_SERVICE); - NetworkRequest request = new NetworkRequest.Builder() - .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) - .build(); - cm.registerNetworkCallback(request, new ConnectivityManager.NetworkCallback() { - @Override - public void onAvailable(Network network) { - dispatchNetworkChange(context); - } - }); - } - - @TargetApi(23) - private void listenForIdle(Context context) { - context.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - dispatchNetworkChange(context); - } - }, new IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)); - } - - void dispatchNetworkChange(Context context) { - if(listener == null) {//shall not be but just be safe - return; - } - //http://developer.android.com/reference/android/net/ConnectivityManager.html#EXTRA_NETWORK_INFO - //Since NetworkInfo can vary based on UID, applications should always obtain network information - // through getActiveNetworkInfo() or getAllNetworkInfo(). - listener.onNetworkChange(getNetworkStatus(context)); - } - - @Override - public int getNetworkStatus(Context context) { - if (Systems.isDozing(context)) { - return NetworkUtil.DISCONNECTED; - } - ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - NetworkInfo netInfo = cm.getActiveNetworkInfo(); - if (netInfo == null) { - return NetworkUtil.DISCONNECTED; - } - - boolean isWifiRequired = PreferenceHelper.getInstance().shouldAutoSendOnWifiOnly(); - boolean isDeviceOnWifi = true; - - if(isWifiRequired){ - isDeviceOnWifi = (netInfo.getType() == ConnectivityManager.TYPE_WIFI); - } - - if ( netInfo.isConnected() && isDeviceOnWifi){ - return NetworkUtil.UNMETERED; - } - - return NetworkUtil.METERED; - } - - @TargetApi(23) - private static IntentFilter getNetworkIntentFilter() { - IntentFilter networkIntentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - networkIntentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); - } - return networkIntentFilter; - } - - @Override - public void setListener(Listener listener) { - this.listener = listener; - } -} \ No newline at end of file diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/sftp/SFTPManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/sftp/SFTPManager.java index ec6fb68e0..582ab578c 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/sftp/SFTPManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/sftp/SFTPManager.java @@ -3,9 +3,6 @@ import androidx.work.OneTimeWorkRequest; import androidx.work.WorkManager; -import com.birbit.android.jobqueue.CancelResult; -import com.birbit.android.jobqueue.JobManager; -import com.birbit.android.jobqueue.TagConstraint; import com.mendhak.gpslogger.common.AppSettings; import com.mendhak.gpslogger.common.PreferenceHelper; import com.mendhak.gpslogger.common.Strings; From d3e3e6ca28c4378e8b0ab7f112ec729e19348733 Mon Sep 17 00:00:00 2001 From: mendhak Date: Sun, 17 Mar 2024 18:23:51 +0000 Subject: [PATCH 36/73] Remove redundant logging line --- .../main/java/com/mendhak/gpslogger/senders/sftp/SFTPWorker.java | 1 - 1 file changed, 1 deletion(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/sftp/SFTPWorker.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/sftp/SFTPWorker.java index 2438a78a3..5fbcdbc95 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/sftp/SFTPWorker.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/sftp/SFTPWorker.java @@ -134,7 +134,6 @@ public Result doWork() { sftpException.fingerprint = session.getHostKey().getFingerPrint(jsch); EventBus.getDefault().post(sftpException.failed(jex.getMessage(), jex)); } else { - LOG.error(jex.getMessage(), jex); EventBus.getDefault().post(new UploadEvents.SFTP().failed(jex.getMessage(), jex)); } From 0a8972ddceff8f3bef80bb341a1a74947a82331d Mon Sep 17 00:00:00 2001 From: mendhak Date: Sun, 17 Mar 2024 18:27:50 +0000 Subject: [PATCH 37/73] Remove redundant commented code --- .../mendhak/gpslogger/senders/osm/OpenStreetMapWorker.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/osm/OpenStreetMapWorker.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/osm/OpenStreetMapWorker.java index c8f38d4ec..125911722 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/osm/OpenStreetMapWorker.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/osm/OpenStreetMapWorker.java @@ -73,7 +73,6 @@ public Result doWork() { @Override public void execute(@Nullable String accessToken, @Nullable String idToken, @Nullable AuthorizationException ex) { if (ex != null){ -// EventBus.getDefault().post(new UploadEvents.OpenStreetMap().failed(ex.toJsonString(), ex)); taskDone.set(true); LOG.error(ex.toJsonString(), ex); return; @@ -94,8 +93,6 @@ public void execute(@Nullable String accessToken, @Nullable String idToken, @Nul LOG.error("Failed to fetch Access Token for OpenStreetMap. Stopping this job."); success = false; failureMessage = "Failed to fetch Access Token for OpenStreetMap."; -// EventBus.getDefault().post(new UploadEvents.OpenStreetMap().failed("Failed to fetch Access Token for OpenStreetMap.")); -// return Result.failure(); } else { @@ -138,7 +135,7 @@ public void execute(@Nullable String accessToken, @Nullable String idToken, @Nul if(body != null){ failureMessage = body.string(); } -// body.close(); + EventBus.getDefault().post(new UploadEvents.OpenStreetMap().failed()); success = false; From 0cc8017f271e2ae5340707e54cef1c1e606ed027 Mon Sep 17 00:00:00 2001 From: mendhak Date: Sun, 17 Mar 2024 18:52:16 +0000 Subject: [PATCH 38/73] Move entire workmanager and workrequest queueing into one method call --- .../com/mendhak/gpslogger/common/Systems.java | 13 ++++++++---- .../senders/customurl/CustomUrlManager.java | 13 +++--------- .../senders/dropbox/DropBoxManager.java | 11 ++++------ .../senders/email/AutoEmailManager.java | 15 ++----------- .../gpslogger/senders/ftp/FtpManager.java | 15 ++----------- .../googledrive/GoogleDriveManager.java | 10 ++------- .../senders/opengts/OpenGTSManager.java | 21 ++++--------------- .../senders/osm/OpenStreetMapManager.java | 8 +------ .../senders/owncloud/OwnCloudManager.java | 12 +++-------- .../gpslogger/senders/sftp/SFTPManager.java | 8 ++----- 10 files changed, 32 insertions(+), 94 deletions(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java b/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java index d09a70e65..5f077c2a7 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java @@ -42,8 +42,10 @@ import androidx.work.BackoffPolicy; import androidx.work.Constraints; import androidx.work.Data; +import androidx.work.ExistingWorkPolicy; import androidx.work.NetworkType; import androidx.work.OneTimeWorkRequest; +import androidx.work.WorkManager; import com.mendhak.gpslogger.common.slf4j.Logs; @@ -241,15 +243,16 @@ else if(appThemeSetting.equalsIgnoreCase("light")){ } /** - * Returns a OneTimeWorkRequest with the given worker class and data map. The constraints are set to + * Starts a OneTimeWorkRequest with the given worker class and data map and tag. The constraints are set to * UNMETERED network type if the user has set the app to only send on wifi. Otherwise it is set to * CONNECTED. The initial delay is set to 1 second to avoid the work being enqueued immediately. - * The backoff criteria is set to exponential with a 30 second initial delay. + * The backoff criteria is set to exponential with a 30 second initial delay. The tag is used to + * uniquely identify the work request, and it replaces any existing work with the same tag. * @param workerClass * @param dataMap * @return */ - public static OneTimeWorkRequest getBasicOneTimeWorkRequest(Class workerClass, HashMap dataMap) { + public static void startWorkManagerRequest(Class workerClass, HashMap dataMap, String tag) { androidx.work.Data data = new Data.Builder().putAll(dataMap).build(); @@ -264,6 +267,8 @@ public static OneTimeWorkRequest getBasicOneTimeWorkRequest(Class workerClass, H .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, java.util.concurrent.TimeUnit.SECONDS) .setInputData(data) .build(); - return workRequest; + + WorkManager.getInstance(AppSettings.getInstance()) + .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); } } diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java index 916b86ffd..114580a92 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java @@ -3,10 +3,6 @@ import android.location.Location; import android.os.Bundle; -import androidx.work.ExistingWorkPolicy; -import androidx.work.OneTimeWorkRequest; -import androidx.work.WorkManager; -import com.mendhak.gpslogger.common.AppSettings; import com.mendhak.gpslogger.common.BundleConstants; import com.mendhak.gpslogger.common.PreferenceHelper; import com.mendhak.gpslogger.common.SerializableLocation; @@ -54,9 +50,7 @@ public void uploadFile(List files) { put("callbackType", "customUrl"); }}; - OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(CustomUrlWorker.class, dataMap); - WorkManager.getInstance(AppSettings.getInstance()) - .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); + Systems.startWorkManagerRequest(CustomUrlWorker.class, dataMap, tag); } } @@ -169,9 +163,8 @@ public void sendByHttp(String url, String method, String body, String headers, S put("callbackType", "customUrl"); }}; - OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(CustomUrlWorker.class, dataMap); - WorkManager.getInstance(AppSettings.getInstance()) - .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); + Systems.startWorkManagerRequest(CustomUrlWorker.class, dataMap, tag); + } private String getFormattedTextblock(String textToFormat, SerializableLocation loc) throws Exception { diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/dropbox/DropBoxManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/dropbox/DropBoxManager.java index 89f90affb..3c9aa7cc8 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/dropbox/DropBoxManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/dropbox/DropBoxManager.java @@ -21,14 +21,10 @@ import android.content.Context; - -import androidx.work.OneTimeWorkRequest; -import androidx.work.WorkManager; - import com.dropbox.core.*; import com.dropbox.core.android.Auth; import com.dropbox.core.oauth.DbxCredential; -import com.mendhak.gpslogger.common.AppSettings; + import com.mendhak.gpslogger.common.PreferenceHelper; import com.mendhak.gpslogger.common.Strings; import com.mendhak.gpslogger.common.Systems; @@ -39,6 +35,7 @@ import java.io.File; import java.util.HashMap; import java.util.List; +import java.util.Objects; public class DropBoxManager extends FileSender { @@ -111,9 +108,9 @@ public void uploadFile(final String fileName) { HashMap dataMap = new HashMap(){{ put("fileName", fileName); }}; + String tag = String.valueOf(Objects.hashCode(fileName)); + Systems.startWorkManagerRequest(DropboxWorker.class, dataMap, tag); - OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(DropboxWorker.class, dataMap); - WorkManager.getInstance(AppSettings.getInstance()).enqueue(workRequest); } @Override diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/email/AutoEmailManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/email/AutoEmailManager.java index 2b91b96de..b7770bda6 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/email/AutoEmailManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/email/AutoEmailManager.java @@ -18,12 +18,6 @@ */ package com.mendhak.gpslogger.senders.email; -import androidx.work.ExistingWorkPolicy; -import androidx.work.OneTimeWorkRequest; -import androidx.work.WorkManager; - - -import com.mendhak.gpslogger.common.AppSettings; import com.mendhak.gpslogger.common.PreferenceHelper; import com.mendhak.gpslogger.common.Strings; import com.mendhak.gpslogger.common.Systems; @@ -61,10 +55,7 @@ public void uploadFile(List files) { }}; String tag = String.valueOf(Objects.hashCode(fileNames)) ; - OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(AutoEmailWorker.class, dataMap); - WorkManager.getInstance(AppSettings.getInstance()) - .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); - + Systems.startWorkManagerRequest(AutoEmailWorker.class, dataMap, tag); } @Override @@ -95,9 +86,7 @@ public void sendTestEmail() { put("fileNames", new String[]{}); }}; String tag = String.valueOf(Objects.hashCode(new String[]{})) ; - OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(AutoEmailWorker.class, dataMap); - WorkManager.getInstance(AppSettings.getInstance()) - .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); + Systems.startWorkManagerRequest(AutoEmailWorker.class, dataMap, tag); } @Override diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/ftp/FtpManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/ftp/FtpManager.java index 682fb5efd..09b23fbe8 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/ftp/FtpManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/ftp/FtpManager.java @@ -21,12 +21,6 @@ package com.mendhak.gpslogger.senders.ftp; - -import androidx.work.ExistingWorkPolicy; -import androidx.work.OneTimeWorkRequest; -import androidx.work.WorkManager; - -import com.mendhak.gpslogger.common.AppSettings; import com.mendhak.gpslogger.common.PreferenceHelper; import com.mendhak.gpslogger.common.Systems; import com.mendhak.gpslogger.common.events.UploadEvents; @@ -62,9 +56,7 @@ public void testFtp() { }}; String tag = String.valueOf(Objects.hashCode(testFile)) ; - OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(FtpWorker.class, dataMap); - WorkManager.getInstance(AppSettings.getInstance()) - .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); + Systems.startWorkManagerRequest(FtpWorker.class, dataMap, tag); } catch (Exception ex) { EventBus.getDefault().post(new UploadEvents.Ftp().failed(ex.getMessage(), ex)); @@ -108,10 +100,7 @@ public void uploadFile(final File f) { }}; String tag = String.valueOf(Objects.hashCode(f)) ; - OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(FtpWorker.class, dataMap); - WorkManager.getInstance(AppSettings.getInstance()) - .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); - + Systems.startWorkManagerRequest(FtpWorker.class, dataMap, tag); } @Override diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/googledrive/GoogleDriveManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/googledrive/GoogleDriveManager.java index cb7c73135..fbe1652c5 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/googledrive/GoogleDriveManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/googledrive/GoogleDriveManager.java @@ -3,11 +3,6 @@ import android.content.Context; import android.net.Uri; -import androidx.work.ExistingWorkPolicy; -import androidx.work.OneTimeWorkRequest; -import androidx.work.WorkManager; - -import com.mendhak.gpslogger.common.AppSettings; import com.mendhak.gpslogger.common.PreferenceHelper; import com.mendhak.gpslogger.common.Strings; import com.mendhak.gpslogger.common.Systems; @@ -96,9 +91,8 @@ public void uploadFile(File fileToUpload) { put("filePath", fileToUpload.getAbsolutePath()); }}; - OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(GoogleDriveWorker.class, dataMap); - WorkManager.getInstance(AppSettings.getInstance()) - .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); + Systems.startWorkManagerRequest(GoogleDriveWorker.class, dataMap, tag); + } @Override diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java index ca24b374e..b140477b9 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/opengts/OpenGTSManager.java @@ -19,10 +19,6 @@ package com.mendhak.gpslogger.senders.opengts; -import androidx.work.ExistingWorkPolicy; -import androidx.work.OneTimeWorkRequest; -import androidx.work.WorkManager; - import com.mendhak.gpslogger.common.*; import com.mendhak.gpslogger.common.slf4j.Logs; import com.mendhak.gpslogger.loggers.customurl.CustomUrlRequest; @@ -66,9 +62,7 @@ public void uploadFile(List files) { HashMap dataMap = new HashMap(){{ put("gpxFilePath", f.getAbsolutePath()); }}; - OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(OpenGtsUdpWorker.class, dataMap); - WorkManager.getInstance(AppSettings.getInstance()) - .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); + Systems.startWorkManagerRequest(OpenGtsUdpWorker.class, dataMap, tag); } else { String tag = String.valueOf(Objects.hashCode(f.getName())); @@ -76,9 +70,7 @@ public void uploadFile(List files) { put("gpxFilePath", f.getAbsolutePath()); put("callbackType", "opengts"); }}; - OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(CustomUrlWorker.class, dataMap); - WorkManager.getInstance(AppSettings.getInstance()) - .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); + Systems.startWorkManagerRequest(CustomUrlWorker.class, dataMap, tag); } } @@ -105,9 +97,7 @@ public void sendLocations(SerializableLocation[] locations){ put("locations", serializedLocations); }}; - OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(OpenGtsUdpWorker.class, dataMap); - WorkManager.getInstance(AppSettings.getInstance()) - .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); + Systems.startWorkManagerRequest(OpenGtsUdpWorker.class, dataMap, tag); } else { sendByHttp(deviceId, accountName, locations, communication, path, server, port); @@ -132,10 +122,7 @@ void sendByHttp(String deviceId, String accountName, SerializableLocation[] loca put("callbackType", "opengts"); }}; - OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(CustomUrlWorker.class, dataMap); - WorkManager.getInstance(AppSettings.getInstance()) - .enqueueUniqueWork(tag, ExistingWorkPolicy.REPLACE, workRequest); - + Systems.startWorkManagerRequest(CustomUrlWorker.class, dataMap, tag); } diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/osm/OpenStreetMapManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/osm/OpenStreetMapManager.java index 290a8d33f..e1f0a9e77 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/osm/OpenStreetMapManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/osm/OpenStreetMapManager.java @@ -22,10 +22,6 @@ import android.content.Context; import android.net.Uri; -import androidx.work.OneTimeWorkRequest; -import androidx.work.WorkManager; - -import com.mendhak.gpslogger.common.AppSettings; import com.mendhak.gpslogger.common.PreferenceHelper; import com.mendhak.gpslogger.common.Strings; import com.mendhak.gpslogger.common.Systems; @@ -136,9 +132,7 @@ public void uploadFile(String fileName) { put("filePath", chosenFile.getAbsolutePath()); }}; - OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(OpenStreetMapWorker.class, dataMap); - WorkManager.getInstance(AppSettings.getInstance()) - .enqueueUniqueWork(tag, androidx.work.ExistingWorkPolicy.REPLACE, workRequest); + Systems.startWorkManagerRequest(OpenStreetMapWorker.class, dataMap, tag); } diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/owncloud/OwnCloudManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/owncloud/OwnCloudManager.java index 114c3394a..531351eb3 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/owncloud/OwnCloudManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/owncloud/OwnCloudManager.java @@ -19,10 +19,7 @@ package com.mendhak.gpslogger.senders.owncloud; -import androidx.work.OneTimeWorkRequest; -import androidx.work.WorkManager; -import com.mendhak.gpslogger.common.AppSettings; import com.mendhak.gpslogger.common.PreferenceHelper; import com.mendhak.gpslogger.common.Strings; import com.mendhak.gpslogger.common.Systems; @@ -57,9 +54,7 @@ public void testOwnCloud() { HashMap dataMap = new HashMap(){{ put("filePath", testFile.getAbsolutePath()); }}; - OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(OwnCloudWorker.class, dataMap); - WorkManager.getInstance(AppSettings.getInstance()) - .enqueueUniqueWork(tag, androidx.work.ExistingWorkPolicy.REPLACE, workRequest); + Systems.startWorkManagerRequest(OwnCloudWorker.class, dataMap, tag); } catch (Exception ex) { EventBus.getDefault().post(new UploadEvents.Ftp().failed()); @@ -111,9 +106,8 @@ public void uploadFile(final File f) HashMap dataMap = new HashMap(){{ put("filePath", f.getAbsolutePath()); }}; - OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(OwnCloudWorker.class, dataMap); - WorkManager.getInstance(AppSettings.getInstance()) - .enqueueUniqueWork(tag, androidx.work.ExistingWorkPolicy.REPLACE, workRequest); + Systems.startWorkManagerRequest(OwnCloudWorker.class, dataMap, tag); + } @Override diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/sftp/SFTPManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/sftp/SFTPManager.java index 582ab578c..fe40b426c 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/sftp/SFTPManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/sftp/SFTPManager.java @@ -1,9 +1,6 @@ package com.mendhak.gpslogger.senders.sftp; -import androidx.work.OneTimeWorkRequest; -import androidx.work.WorkManager; -import com.mendhak.gpslogger.common.AppSettings; import com.mendhak.gpslogger.common.PreferenceHelper; import com.mendhak.gpslogger.common.Strings; import com.mendhak.gpslogger.common.Systems; @@ -35,9 +32,8 @@ public void uploadFile(final File file){ HashMap dataMap = new HashMap(){{ put("filePath", file.getAbsolutePath()); }}; - OneTimeWorkRequest workRequest = Systems.getBasicOneTimeWorkRequest(SFTPWorker.class, dataMap); - WorkManager.getInstance(AppSettings.getInstance()) - .enqueueUniqueWork(tag, androidx.work.ExistingWorkPolicy.REPLACE, workRequest); + Systems.startWorkManagerRequest(SFTPWorker.class, dataMap, tag); + } @Override From 758f1e04cd62c9dc8c7b214d8603cc31fcda9885 Mon Sep 17 00:00:00 2001 From: mendhak Date: Sun, 17 Mar 2024 19:05:07 +0000 Subject: [PATCH 39/73] Make dropbox worker use filepath just like the other workers --- .../gpslogger/senders/dropbox/DropBoxManager.java | 8 ++++---- .../gpslogger/senders/dropbox/DropboxWorker.java | 11 +++++------ .../settings/DropboxAuthorizationFragment.java | 2 +- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/dropbox/DropBoxManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/dropbox/DropBoxManager.java index 3c9aa7cc8..6bb0395dd 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/dropbox/DropBoxManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/dropbox/DropBoxManager.java @@ -84,7 +84,7 @@ public void unLink() { public void uploadFile(List files) { for (File f : files) { LOG.debug(f.getName()); - uploadFile(f.getName()); + uploadFile(f); } } @@ -103,12 +103,12 @@ public String getName() { return SenderNames.DROPBOX; } - public void uploadFile(final String fileName) { + public void uploadFile(File fileToUpload) { HashMap dataMap = new HashMap(){{ - put("fileName", fileName); + put("filePath", fileToUpload.getAbsolutePath()); }}; - String tag = String.valueOf(Objects.hashCode(fileName)); + String tag = String.valueOf(Objects.hashCode(fileToUpload.getName())); Systems.startWorkManagerRequest(DropboxWorker.class, dataMap, tag); } diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/dropbox/DropboxWorker.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/dropbox/DropboxWorker.java index f8dc1a4a2..643f19da9 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/dropbox/DropboxWorker.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/dropbox/DropboxWorker.java @@ -34,18 +34,17 @@ public DropboxWorker(@NonNull Context context, @NonNull WorkerParameters workerP @Override public Result doWork() { - String fileName = getInputData().getString("fileName"); - if(Strings.isNullOrEmpty(fileName)) { + String filePath = getInputData().getString("filePath"); + if(Strings.isNullOrEmpty(filePath)) { EventBus.getDefault().post(new UploadEvents.Dropbox().failed("Dropbox upload failed", new Throwable("Nothing to upload."))); return Result.failure(); } - File gpsDir = new File(PreferenceHelper.getInstance().getGpsLoggerFolder()); - File gpxFile = new File(gpsDir, fileName); + File fileToUpload = new File(filePath); try { LOG.debug("Beginning upload to dropbox..."); - InputStream inputStream = new FileInputStream(gpxFile); + InputStream inputStream = new FileInputStream(fileToUpload); DbxRequestConfig requestConfig = DbxRequestConfig.newBuilder("GPSLogger").build(); DbxClientV2 mDbxClient; @@ -58,7 +57,7 @@ public Result doWork() { mDbxClient = new DbxClientV2(requestConfig, PreferenceHelper.getInstance().getDropboxLongLivedAccessKey()); } - mDbxClient.files().uploadBuilder("/" + fileName).withMode(WriteMode.OVERWRITE).uploadAndFinish(inputStream); + mDbxClient.files().uploadBuilder("/" + fileToUpload.getName()).withMode(WriteMode.OVERWRITE).uploadAndFinish(inputStream); EventBus.getDefault().post(new UploadEvents.Dropbox().succeeded()); LOG.info("Dropbox - file uploaded"); diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/DropboxAuthorizationFragment.java b/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/DropboxAuthorizationFragment.java index 657c36687..84e078373 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/DropboxAuthorizationFragment.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/DropboxAuthorizationFragment.java @@ -128,7 +128,7 @@ private void uploadTestFile() { try { File testFile = Files.createTestFile(); - manager.uploadFile(testFile.getName()); + manager.uploadFile(testFile); } catch (Exception ex) { LOG.error("Could not create local test file", ex); From 38d52a520d3a8ba1e2f089ad8df6dbf1831b6cc7 Mon Sep 17 00:00:00 2001 From: mendhak Date: Tue, 19 Mar 2024 19:07:59 +0000 Subject: [PATCH 40/73] Android gradle plugin and version changes --- gpslogger/build.gradle | 7 +++---- gpslogger/src/main/AndroidManifest.xml | 4 +--- gradle.properties | 5 +++++ gradle/wrapper/gradle-wrapper.properties | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/gpslogger/build.gradle b/gpslogger/build.gradle index 8f5f7cbb5..78d7896f3 100644 --- a/gpslogger/build.gradle +++ b/gpslogger/build.gradle @@ -7,7 +7,7 @@ buildscript { } } dependencies { - classpath 'com.android.tools.build:gradle:7.2.0' + classpath 'com.android.tools.build:gradle:8.1.0' classpath 'org.jacoco:org.jacoco.core:0.7.4.201502262128' classpath "com.moowork.gradle:gradle-node-plugin:0.13" @@ -45,7 +45,6 @@ android { //noinspection ExpiredTargetSdkVersion targetSdkVersion 30 compileSdk 34 - versionCode 130 versionName "130-workmgr" @@ -216,7 +215,7 @@ tasks.withType(Test) { } } -tasks.whenTaskAdded { task -> +tasks.configureEach { task -> //Don't run lint. Takes too long. if (task.name.contains("lint")) { task.enabled = false @@ -247,7 +246,7 @@ task buildTranslationArray { } preBuild.dependsOn buildTranslationArray -tasks.whenTaskAdded { task -> +tasks.configureEach { task -> if (task.name == 'preDebugBuild' || task.name == 'preReleaseBuild') { task.dependsOn buildTranslationArray } diff --git a/gpslogger/src/main/AndroidManifest.xml b/gpslogger/src/main/AndroidManifest.xml index c1858c62f..9191d823a 100644 --- a/gpslogger/src/main/AndroidManifest.xml +++ b/gpslogger/src/main/AndroidManifest.xml @@ -1,9 +1,7 @@ + android:installLocation="auto"> diff --git a/gradle.properties b/gradle.properties index d9244dbe8..28ae68cd0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,3 +5,8 @@ org.gradle.jvmargs=-XX:MaxMetaspaceSize=512m android.useAndroidX=true android.enableJetifier=true android.jetifier.ignorelist=jsch-0.1.67.jar +android.enableR8.fullMode=false +android.defaults.buildfeatures.buildconfig=true +android.nonTransitiveRClass=false +android.nonFinalResIds=false +org.gradle.unsafe.configuration-cache=true diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2e6e5897b..da1db5f04 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 48f2c3f3b8a3e352220dd1d04f4dc26a398f8b7b Mon Sep 17 00:00:00 2001 From: mendhak Date: Tue, 19 Mar 2024 19:14:20 +0000 Subject: [PATCH 41/73] If the auto send interval is set to 0, don't set a timer for it. Issue #1129 --- .../src/main/java/com/mendhak/gpslogger/GpsLoggingService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java b/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java index d215ec3e1..55408fb61 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java @@ -381,7 +381,7 @@ private void autoSendLogFile(@Nullable String formattedFileName) { private void resetAutoSendTimersIfNecessary() { - if (session.getAutoSendDelay() != preferenceHelper.getAutoSendInterval()) { + if (session.getAutoSendDelay() != preferenceHelper.getAutoSendInterval() && preferenceHelper.getAutoSendInterval() > 0) { session.setAutoSendDelay(preferenceHelper.getAutoSendInterval()); setupAutoSendTimers(); } From fe56ceee6505802ac2986f5092f7bb0710220b62 Mon Sep 17 00:00:00 2001 From: mendhak Date: Tue, 19 Mar 2024 21:11:19 +0000 Subject: [PATCH 42/73] Run tests without cache and add a github action reporter --- .github/workflows/android.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index c32963628..178787c65 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -18,7 +18,11 @@ jobs: java-version: '17' - name: Build with Gradle - run: ./gradlew assembleDebugUnitTest -Dpre-dex=false + run: ./gradlew assembleDebugUnitTest -Dpre-dex=false --no-configuration-cache - name: Run Unit Tests - run: ./gradlew testDebugUnitTest -Dpre-dex=false -q + run: ./gradlew testDebugUnitTest -Dpre-dex=false -q --no-configuration-cache + + - name: Android Test Report + uses: asadmansr/android-test-report-action@v1.2.0 + if: ${{ always() }} \ No newline at end of file From 87ba3f9f5342ef283197a5705a0f585bcdd6f025 Mon Sep 17 00:00:00 2001 From: mendhak Date: Tue, 19 Mar 2024 21:15:05 +0000 Subject: [PATCH 43/73] removing test reporter, not adding much over what I've already got --- .github/workflows/android.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 178787c65..773b9958c 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -22,7 +22,3 @@ jobs: - name: Run Unit Tests run: ./gradlew testDebugUnitTest -Dpre-dex=false -q --no-configuration-cache - - - name: Android Test Report - uses: asadmansr/android-test-report-action@v1.2.0 - if: ${{ always() }} \ No newline at end of file From db8ded350f6fb81bab4efbae9de82d3d0fe4d341 Mon Sep 17 00:00:00 2001 From: mendhak Date: Sat, 23 Mar 2024 09:38:42 +0000 Subject: [PATCH 44/73] Clarify that auto send also sends when file name changes, based on how dynamic the name is, or daily file option. Issue #1129 --- gpslogger/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpslogger/src/main/res/values/strings.xml b/gpslogger/src/main/res/values/strings.xml index 166c520a7..8eb32fcdf 100644 --- a/gpslogger/src/main/res/values/strings.xml +++ b/gpslogger/src/main/res/values/strings.xml @@ -283,7 +283,7 @@ Allow auto sending - Allows sending the files to various targets + Allows sending the files to various targets, at a defined frequency, or if the name changes due to custom naming How frequently log files should be sent, in minutes. Set to 0 to disable this. How often? From 66b27579d45e0a51805b16ba4b0beaae0fa71a52 Mon Sep 17 00:00:00 2001 From: mendhak Date: Sat, 23 Mar 2024 11:22:37 +0000 Subject: [PATCH 45/73] Unnecessary check - the auto send timer is already being reset to zero if necessary --- .../src/main/java/com/mendhak/gpslogger/GpsLoggingService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java b/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java index 55408fb61..d215ec3e1 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java @@ -381,7 +381,7 @@ private void autoSendLogFile(@Nullable String formattedFileName) { private void resetAutoSendTimersIfNecessary() { - if (session.getAutoSendDelay() != preferenceHelper.getAutoSendInterval() && preferenceHelper.getAutoSendInterval() > 0) { + if (session.getAutoSendDelay() != preferenceHelper.getAutoSendInterval()) { session.setAutoSendDelay(preferenceHelper.getAutoSendInterval()); setupAutoSendTimers(); } From 112194f6a1850c2e848b203727ea1698bac1298e Mon Sep 17 00:00:00 2001 From: mendhak Date: Thu, 28 Mar 2024 20:59:59 +0000 Subject: [PATCH 46/73] Option to enable or disable prompting before starting logging, if OSM sending is enabled. Issue #1131 --- .../com/mendhak/gpslogger/common/PreferenceHelper.java | 7 +++++++ .../java/com/mendhak/gpslogger/common/PreferenceNames.java | 1 + .../ui/fragments/display/GenericViewFragment.java | 5 +++-- .../ui/fragments/settings/OSMAuthorizationFragment.java | 5 ++++- gpslogger/src/main/res/values/strings.xml | 1 + gpslogger/src/main/res/xml/osmsettings.xml | 5 +++++ 6 files changed, 21 insertions(+), 3 deletions(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/common/PreferenceHelper.java b/gpslogger/src/main/java/com/mendhak/gpslogger/common/PreferenceHelper.java index b9899f2ce..02d385e25 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/common/PreferenceHelper.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/common/PreferenceHelper.java @@ -992,6 +992,13 @@ public boolean isOsmAutoSendEnabled() { } + /** + * Whether to prompt user for OSM details before starting logging + */ + @ProfilePreference(name= PreferenceNames.OPENSTREETMAP_PROMPT_WHEN_LOGGING_STARTS) + public boolean shouldPromptForOSMDetailsWhenLoggingStarts() { + return prefs.getBoolean(PreferenceNames.OPENSTREETMAP_PROMPT_WHEN_LOGGING_STARTS, false); + } diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/common/PreferenceNames.java b/gpslogger/src/main/java/com/mendhak/gpslogger/common/PreferenceNames.java index b4cbbe59f..6cd6d300e 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/common/PreferenceNames.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/common/PreferenceNames.java @@ -89,6 +89,7 @@ public class PreferenceNames { public static final String OPENSTREETMAP_DESCRIPTION = "osm_description"; public static final String OPENSTREETMAP_TAGS = "osm_tags"; public static final String OPENSTREETMAP_VISIBILITY = "osm_visibility"; + public static final String OPENSTREETMAP_PROMPT_WHEN_LOGGING_STARTS = "osm_promptfordetails_when_logging_starts"; public static final String AUTOSEND_DROPBOX_ENABLED = "dropbox_enabled"; public static final String DROPBOX_LONG_LIVED_ACCESS_TOKEN = "DROPBOX_ACCESS_KEY"; public static final String DROPBOX_REFRESH_TOKEN = "DROPBOX_REFRESH_TOKEN"; diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/display/GenericViewFragment.java b/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/display/GenericViewFragment.java index 259477a55..9b7cac6d8 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/display/GenericViewFragment.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/display/GenericViewFragment.java @@ -37,7 +37,6 @@ import com.mendhak.gpslogger.senders.FileSenderFactory; import com.mendhak.gpslogger.ui.Dialogs; import de.greenrobot.event.EventBus; -import eltos.simpledialogfragment.SimpleDialog; import eltos.simpledialogfragment.form.FormElement; import eltos.simpledialogfragment.form.Hint; import eltos.simpledialogfragment.form.Input; @@ -130,7 +129,9 @@ public void requestToggleLogging() { //If the user needs to be prompted about OpenStreetMap settings, build some form elements for it. if(preferenceHelper.isAutoSendEnabled() && preferenceHelper.isOsmAutoSendEnabled() - && FileSenderFactory.getOsmSender().isAutoSendAvailable()){ + && FileSenderFactory.getOsmSender().isAutoSendAvailable() + && preferenceHelper.shouldPromptForOSMDetailsWhenLoggingStarts() + ){ formElements.add(Hint.plain(R.string.osm_setup_title)); formElements.addAll(Dialogs.getOpenStreetMapFormElementsForDialog(preferenceHelper)); } diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/OSMAuthorizationFragment.java b/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/OSMAuthorizationFragment.java index 2444b7a27..7b2b28960 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/OSMAuthorizationFragment.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/OSMAuthorizationFragment.java @@ -105,6 +105,8 @@ private void setPreferencesState() { tagsPref.setOnPreferenceClickListener(this); tagsPref.setSummary(preferenceHelper.getOSMTags()); + Preference promptPref = findPreference(PreferenceNames.OPENSTREETMAP_PROMPT_WHEN_LOGGING_STARTS); + Preference resetPref = findPreference("osm_resetauth"); if (!manager.isOsmAuthorized()) { @@ -113,13 +115,14 @@ private void setPreferencesState() { visibilityPref.setEnabled(false); descriptionPref.setEnabled(false); tagsPref.setEnabled(false); + promptPref.setEnabled(false); } else { resetPref.setTitle(R.string.osm_resetauth); resetPref.setSummary(""); visibilityPref.setEnabled(true); descriptionPref.setEnabled(true); tagsPref.setEnabled(true); - + promptPref.setEnabled(true); } resetPref.setOnPreferenceClickListener(this); diff --git a/gpslogger/src/main/res/values/strings.xml b/gpslogger/src/main/res/values/strings.xml index 8eb32fcdf..02b4a6860 100644 --- a/gpslogger/src/main/res/values/strings.xml +++ b/gpslogger/src/main/res/values/strings.xml @@ -492,5 +492,6 @@ Edit annotation button Button color + Prompt for details when logging starts diff --git a/gpslogger/src/main/res/xml/osmsettings.xml b/gpslogger/src/main/res/xml/osmsettings.xml index 71cca00c1..a65018bb0 100644 --- a/gpslogger/src/main/res/xml/osmsettings.xml +++ b/gpslogger/src/main/res/xml/osmsettings.xml @@ -36,5 +36,10 @@ android:title="@string/osm_tags" app:iconSpaceReserved="false" /> + From bbd6fe4fe2d54e346d5eb34eaf069c3a09e9cce2 Mon Sep 17 00:00:00 2001 From: mendhak Date: Thu, 28 Mar 2024 21:04:30 +0000 Subject: [PATCH 47/73] Changelog for 1313 --- fastlane/metadata/android/en-US/changelogs/131.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 fastlane/metadata/android/en-US/changelogs/131.txt diff --git a/fastlane/metadata/android/en-US/changelogs/131.txt b/fastlane/metadata/android/en-US/changelogs/131.txt new file mode 100644 index 000000000..b0f3f9355 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/131.txt @@ -0,0 +1,3 @@ +* OpenStreetMap option to enable or disable being prompted before logging starts +* Internal change, switching to Android WorkManager for sending and logging tasks. +* Clarified autosend description, that a dynamic file name change also results in the file being sent. \ No newline at end of file From 241007078dbd8e19ff64034f889add0f368375ab Mon Sep 17 00:00:00 2001 From: mendhak Date: Thu, 28 Mar 2024 21:04:54 +0000 Subject: [PATCH 48/73] 131 version bump --- gpslogger/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gpslogger/build.gradle b/gpslogger/build.gradle index d97c99353..8b0e2d36b 100644 --- a/gpslogger/build.gradle +++ b/gpslogger/build.gradle @@ -45,8 +45,8 @@ android { //noinspection ExpiredTargetSdkVersion targetSdkVersion 30 compileSdk 34 - versionCode 130 - versionName "130-workmgr" + versionCode 131 + versionName "131-workmgr" manifestPlaceholders = [ appAuthRedirectScheme: 'com.mendhak.gpslogger' From bc2e9d80a95e2d72dfaa12090afddcceb8bd1407 Mon Sep 17 00:00:00 2001 From: mendhak Date: Mon, 1 Apr 2024 18:31:51 +0100 Subject: [PATCH 49/73] Send a broadcast when a file is uploaded using customurl Issue #1083 --- .../java/com/mendhak/gpslogger/GpsLoggingService.java | 2 +- .../java/com/mendhak/gpslogger/common/Systems.java | 10 ++++++++++ .../gpslogger/loggers/customurl/CustomUrlWorker.java | 7 +++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java b/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java index d215ec3e1..add8fe3f9 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java @@ -420,7 +420,7 @@ protected void startLogging() { } private void notifyByBroadcast(boolean loggingStarted) { - LOG.debug("Sending a custom broadcast"); + LOG.debug("Sending a started/stopped broadcast"); String event = (loggingStarted) ? "started" : "stopped"; Intent sendIntent = new Intent(); sendIntent.setAction("com.mendhak.gpslogger.EVENT"); diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java b/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java index 5f077c2a7..7800a2388 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java @@ -271,4 +271,14 @@ public static void startWorkManagerRequest(Class workerClass, HashMap Date: Mon, 1 Apr 2024 18:42:50 +0100 Subject: [PATCH 50/73] Add description for receiving file uploaded event --- assets/text/faq/faq14-tasker-automation.md | 40 ++++++++++++++-------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/assets/text/faq/faq14-tasker-automation.md b/assets/text/faq/faq14-tasker-automation.md index 4513dc856..2838f3331 100644 --- a/assets/text/faq/faq14-tasker-automation.md +++ b/assets/text/faq/faq14-tasker-automation.md @@ -54,20 +54,32 @@ The app comes with a Start and a Stop **shortcut** (long press home screen, add ### Listening to GPSLogger +GPSLogger sends a broadcast start/stop of logging, or file uploaded, which you can receive as an event. + +In Tasker, this would be the `Intent Received` event. +Set the action to `com.mendhak.gpslogger.EVENT`. +You can then access the extras as `%variablename`. + +In Automate, you can use the Broadcast Receive block. +Set the Action to `com.mendhak.gpslogger.EVENT`. +Set the dictionary with broadcast extras to a variable, then access the extras as `myvariable["variablename"]`. + +From there in your task, you can look at the following variables. + +*Start/Stop logging* + +* `gpsloggerevent` - `started` or `stopped` +* `filename` - the base filename that was chosen (no extension) +* `startedtimestamp` - timestamp when logging was started (epoch) +* `duration` - seconds since the current session started +* `distance` - meters travelled since the session started + + +*File uploaded* + +* `gpsloggerevent` - `fileuploaded` +* `filepath` - the full path to the file that was uploaded +* `sendertype` - which sender was used to upload the file, e.g. `customurl`, `ftp`, etc. -(Experimental feature) GPSLogger sends a broadcast start/stop of logging, which you can receive as an event. - -In Tasker, this would look like: - -> Event: Intent Received - Action: com.mendhak.gpslogger.EVENT - -From there in your task, you can look at the following variables - - * `%gpsloggerevent` - `started` or `stopped` - * `%filename` - the base filename that was chosen (no extension) - * `%startedtimestamp` - timestamp when logging was started (epoch) - * `%duration` - seconds since the current session started - * `%distance` - meters travelled since the session started In a custom application, receive the `com.mendhak.gpslogger.EVENT` broadcast and have a look inside the extras. \ No newline at end of file From 1c419506495844523106d16b0f52f32734a77e7c Mon Sep 17 00:00:00 2001 From: mendhak Date: Mon, 1 Apr 2024 19:22:05 +0100 Subject: [PATCH 51/73] Change to file paths, plural. Broadcast from email sender --- assets/text/faq/faq14-tasker-automation.md | 6 +++--- .../src/main/java/com/mendhak/gpslogger/common/Systems.java | 4 ++-- .../gpslogger/loggers/customurl/CustomUrlWorker.java | 3 ++- .../mendhak/gpslogger/senders/email/AutoEmailWorker.java | 2 ++ 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/assets/text/faq/faq14-tasker-automation.md b/assets/text/faq/faq14-tasker-automation.md index 2838f3331..a42f4468d 100644 --- a/assets/text/faq/faq14-tasker-automation.md +++ b/assets/text/faq/faq14-tasker-automation.md @@ -58,11 +58,11 @@ GPSLogger sends a broadcast start/stop of logging, or file uploaded, which you c In Tasker, this would be the `Intent Received` event. Set the action to `com.mendhak.gpslogger.EVENT`. -You can then access the extras as `%variablename`. +You can then access the extras as `%variablename` or `%arrayname1`. In Automate, you can use the Broadcast Receive block. Set the Action to `com.mendhak.gpslogger.EVENT`. -Set the dictionary with broadcast extras to a variable, then access the extras as `myvariable["variablename"]`. +Set the dictionary with broadcast extras to a variable, then access the extras as `myvar["variablename"]` or `myvar["arrayname"][0]`. From there in your task, you can look at the following variables. @@ -78,7 +78,7 @@ From there in your task, you can look at the following variables. *File uploaded* * `gpsloggerevent` - `fileuploaded` -* `filepath` - the full path to the file that was uploaded +* `filepaths` - an array of file paths that were uploaded, even if it's just a single file * `sendertype` - which sender was used to upload the file, e.g. `customurl`, `ftp`, etc. diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java b/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java index 7800a2388..930f7901d 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java @@ -272,12 +272,12 @@ public static void startWorkManagerRequest(Class workerClass, HashMap Date: Mon, 1 Apr 2024 19:28:22 +0100 Subject: [PATCH 52/73] Clarifying internal and external receivers --- .../mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java | 2 ++ .../com/mendhak/gpslogger/senders/dropbox/DropboxWorker.java | 5 +++++ .../com/mendhak/gpslogger/senders/email/AutoEmailWorker.java | 5 ++++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java index 3132cc446..7e0febe0f 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java @@ -103,11 +103,13 @@ public Result doWork() { String gpxFilePath = getInputData().getString("gpxFilePath"); String csvFilePath = getInputData().getString("csvFilePath"); + // Notify external listeners if(!Strings.isNullOrEmpty(gpxFilePath) || !Strings.isNullOrEmpty(csvFilePath)){ String[] filePaths = new String[]{ Strings.isNullOrEmpty(gpxFilePath) ? csvFilePath : gpxFilePath }; Systems.sendFileUploadedBroadcast(getApplicationContext(), filePaths, "customurl"); } + // Notify internal listeners EventBus.getDefault().post(callbackEvent.succeeded()); return Result.success(); } diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/dropbox/DropboxWorker.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/dropbox/DropboxWorker.java index 643f19da9..3c700b46d 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/dropbox/DropboxWorker.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/dropbox/DropboxWorker.java @@ -12,6 +12,7 @@ import com.dropbox.core.v2.files.WriteMode; import com.mendhak.gpslogger.common.PreferenceHelper; import com.mendhak.gpslogger.common.Strings; +import com.mendhak.gpslogger.common.Systems; import com.mendhak.gpslogger.common.events.UploadEvents; import com.mendhak.gpslogger.common.slf4j.Logs; @@ -59,7 +60,11 @@ public Result doWork() { mDbxClient.files().uploadBuilder("/" + fileToUpload.getName()).withMode(WriteMode.OVERWRITE).uploadAndFinish(inputStream); + // Notify internal listeners EventBus.getDefault().post(new UploadEvents.Dropbox().succeeded()); + // Notify external listeners + Systems.sendFileUploadedBroadcast(getApplicationContext(), new String[]{fileToUpload.getAbsolutePath()}, "dropbox"); + LOG.info("Dropbox - file uploaded"); } catch (Exception e) { LOG.error("Could not upload to Dropbox" , e); diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/email/AutoEmailWorker.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/email/AutoEmailWorker.java index 1079b948c..2fef8973b 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/email/AutoEmailWorker.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/email/AutoEmailWorker.java @@ -168,7 +168,10 @@ public void protocolReplyReceived(ProtocolCommandEvent event) { } else { LOG.info("Email - file sent"); + // Notify internal listeners EventBus.getDefault().post(new UploadEvents.AutoEmail().succeeded()); + // Notify external listeners + Systems.sendFileUploadedBroadcast(getApplicationContext(), fileNames, "email"); } } else { @@ -198,7 +201,7 @@ public void protocolReplyReceived(ProtocolCommandEvent event) { } } - Systems.sendFileUploadedBroadcast(getApplicationContext(), fileNames, "email"); + return Result.success(); } From 2d92cd6e44251feacc98ade409121ded63919dd6 Mon Sep 17 00:00:00 2001 From: mendhak Date: Mon, 1 Apr 2024 19:30:39 +0100 Subject: [PATCH 53/73] Send broadcast fileuploaded for FTP --- .../java/com/mendhak/gpslogger/senders/ftp/FtpWorker.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/ftp/FtpWorker.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/ftp/FtpWorker.java index db63cdc9b..f29947b85 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/ftp/FtpWorker.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/ftp/FtpWorker.java @@ -9,6 +9,7 @@ import com.mendhak.gpslogger.common.AppSettings; import com.mendhak.gpslogger.common.PreferenceHelper; import com.mendhak.gpslogger.common.Strings; +import com.mendhak.gpslogger.common.Systems; import com.mendhak.gpslogger.common.events.UploadEvents; import com.mendhak.gpslogger.common.network.Networks; import com.mendhak.gpslogger.common.slf4j.LoggingOutputStream; @@ -62,7 +63,10 @@ public Result doWork() { if (upload(server, username, password, directory, port, useFtps, protocol, implicit, file, file.getName())) { LOG.info("FTP - file uploaded"); + // Notify internal listeners EventBus.getDefault().post(new UploadEvents.Ftp().succeeded()); + // Notify external listeners + Systems.sendFileUploadedBroadcast(getApplicationContext(), new String[]{file.getAbsolutePath()}, "ftp"); return Result.success(); } else { jobResult.ftpMessages = ftpServerResponses; From 26d5aeac2ba904162074ccc3674a7fe4e280921c Mon Sep 17 00:00:00 2001 From: mendhak Date: Mon, 1 Apr 2024 19:31:31 +0100 Subject: [PATCH 54/73] Send broadcast fileuploaded for Google Drive --- .../gpslogger/senders/googledrive/GoogleDriveWorker.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/googledrive/GoogleDriveWorker.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/googledrive/GoogleDriveWorker.java index 2ba339648..c8fb14bb3 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/googledrive/GoogleDriveWorker.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/googledrive/GoogleDriveWorker.java @@ -11,6 +11,7 @@ import com.mendhak.gpslogger.common.AppSettings; import com.mendhak.gpslogger.common.PreferenceHelper; import com.mendhak.gpslogger.common.Strings; +import com.mendhak.gpslogger.common.Systems; import com.mendhak.gpslogger.common.events.UploadEvents; import com.mendhak.gpslogger.common.slf4j.Logs; import com.mendhak.gpslogger.loggers.Files; @@ -149,7 +150,10 @@ public void execute(@Nullable String accessToken, @Nullable String idToken, @Nul } if(success){ + // Notify internal listeners EventBus.getDefault().post(new UploadEvents.GoogleDrive().succeeded()); + // Notify external listeners + Systems.sendFileUploadedBroadcast(getApplicationContext(), new String[]{fileToUpload.getAbsolutePath()}, "googledrive"); return Result.success(); } From 79df7f7d8662241163f69a4572c9f8f52af29a17 Mon Sep 17 00:00:00 2001 From: mendhak Date: Mon, 1 Apr 2024 19:44:06 +0100 Subject: [PATCH 55/73] Rename the callback type which is also used as the sender type in broadcast --- .../mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java | 2 +- .../mendhak/gpslogger/senders/customurl/CustomUrlManager.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java index 7e0febe0f..b8e5d6c5e 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java @@ -106,7 +106,7 @@ public Result doWork() { // Notify external listeners if(!Strings.isNullOrEmpty(gpxFilePath) || !Strings.isNullOrEmpty(csvFilePath)){ String[] filePaths = new String[]{ Strings.isNullOrEmpty(gpxFilePath) ? csvFilePath : gpxFilePath }; - Systems.sendFileUploadedBroadcast(getApplicationContext(), filePaths, "customurl"); + Systems.sendFileUploadedBroadcast(getApplicationContext(), filePaths, getInputData().getString("callbackType")); } // Notify internal listeners diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java index 114580a92..224479a36 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java @@ -47,7 +47,7 @@ public void uploadFile(List files) { HashMap dataMap = new HashMap() {{ put("csvFilePath", f.getAbsolutePath()); - put("callbackType", "customUrl"); + put("callbackType", "customurl"); }}; Systems.startWorkManagerRequest(CustomUrlWorker.class, dataMap, tag); @@ -160,7 +160,7 @@ public void sendByHttp(String url, String method, String body, String headers, S HashMap dataMap = new HashMap() {{ put("urlRequests", new String[]{serializedRequest}); - put("callbackType", "customUrl"); + put("callbackType", "customurl"); }}; Systems.startWorkManagerRequest(CustomUrlWorker.class, dataMap, tag); From 310da53e9ca3abb020f2bae14c20f75e830b0070 Mon Sep 17 00:00:00 2001 From: mendhak Date: Mon, 1 Apr 2024 19:52:11 +0100 Subject: [PATCH 56/73] Notify internal listeners before external listeners --- .../gpslogger/loggers/customurl/CustomUrlWorker.java | 5 +++-- .../mendhak/gpslogger/loggers/opengts/OpenGtsUdpWorker.java | 6 ++++++ .../mendhak/gpslogger/senders/osm/OpenStreetMapWorker.java | 5 +++++ .../mendhak/gpslogger/senders/owncloud/OwnCloudWorker.java | 5 +++++ .../java/com/mendhak/gpslogger/senders/sftp/SFTPWorker.java | 4 ++++ 5 files changed, 23 insertions(+), 2 deletions(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java index b8e5d6c5e..0fa1a8c52 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/customurl/CustomUrlWorker.java @@ -101,6 +101,9 @@ public Result doWork() { if(success) { + // Notify internal listeners + EventBus.getDefault().post(callbackEvent.succeeded()); + String gpxFilePath = getInputData().getString("gpxFilePath"); String csvFilePath = getInputData().getString("csvFilePath"); // Notify external listeners @@ -109,8 +112,6 @@ public Result doWork() { Systems.sendFileUploadedBroadcast(getApplicationContext(), filePaths, getInputData().getString("callbackType")); } - // Notify internal listeners - EventBus.getDefault().post(callbackEvent.succeeded()); return Result.success(); } else { diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/opengts/OpenGtsUdpWorker.java b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/opengts/OpenGtsUdpWorker.java index 9debbc0be..9b082ab6e 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/opengts/OpenGtsUdpWorker.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/opengts/OpenGtsUdpWorker.java @@ -9,6 +9,7 @@ import com.mendhak.gpslogger.common.PreferenceHelper; import com.mendhak.gpslogger.common.SerializableLocation; import com.mendhak.gpslogger.common.Strings; +import com.mendhak.gpslogger.common.Systems; import com.mendhak.gpslogger.common.events.UploadEvents; import com.mendhak.gpslogger.common.slf4j.Logs; import com.mendhak.gpslogger.senders.GpxReader; @@ -55,7 +56,12 @@ else if (serializedLocations != null && serializedLocations.length > 0){ } sendRAW(deviceId, accountName, server, port, locations); } + + // Notify internal listeners EventBus.getDefault().post(new UploadEvents.OpenGTS().succeeded()); + // Notify external listeners + Systems.sendFileUploadedBroadcast(getApplicationContext(), new String[]{gpxFilePath}, "opengts"); + } catch(Exception ex){ LOG.error("Could not send to OpenGTS", ex); diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/osm/OpenStreetMapWorker.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/osm/OpenStreetMapWorker.java index 125911722..f8c2f4076 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/osm/OpenStreetMapWorker.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/osm/OpenStreetMapWorker.java @@ -10,6 +10,7 @@ import com.mendhak.gpslogger.common.AppSettings; import com.mendhak.gpslogger.common.PreferenceHelper; import com.mendhak.gpslogger.common.Strings; +import com.mendhak.gpslogger.common.Systems; import com.mendhak.gpslogger.common.events.UploadEvents; import com.mendhak.gpslogger.common.network.Networks; import com.mendhak.gpslogger.common.slf4j.Logs; @@ -150,7 +151,11 @@ public void execute(@Nullable String accessToken, @Nullable String idToken, @Nul if(success){ + + // Notify internal listeners EventBus.getDefault().post(new UploadEvents.OpenStreetMap().succeeded()); + // Notify external listeners + Systems.sendFileUploadedBroadcast(getApplicationContext(), new String[]{fileToUpload.getAbsolutePath()}, "openstreetmap"); return Result.success(); } else { diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/owncloud/OwnCloudWorker.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/owncloud/OwnCloudWorker.java index d4825f3cc..7490f6a4a 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/owncloud/OwnCloudWorker.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/owncloud/OwnCloudWorker.java @@ -10,6 +10,7 @@ import com.mendhak.gpslogger.common.AppSettings; import com.mendhak.gpslogger.common.PreferenceHelper; import com.mendhak.gpslogger.common.Strings; +import com.mendhak.gpslogger.common.Systems; import com.mendhak.gpslogger.common.events.UploadEvents; import com.mendhak.gpslogger.common.network.LocalX509TrustManager; import com.mendhak.gpslogger.common.network.Networks; @@ -127,7 +128,11 @@ public Result doWork() { } else { LOG.info("OwnCloud - file uploaded"); + + // Notify internal listeners EventBus.getDefault().post(new UploadEvents.OwnCloud().succeeded()); + // Notify external listeners + Systems.sendFileUploadedBroadcast(getApplicationContext(), new String[]{fileToUpload.getAbsolutePath()}, "owncloud"); return Result.success(); } } diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/sftp/SFTPWorker.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/sftp/SFTPWorker.java index 5fbcdbc95..9cedc5896 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/sftp/SFTPWorker.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/sftp/SFTPWorker.java @@ -15,6 +15,7 @@ import com.jcraft.jsch.SftpException; import com.mendhak.gpslogger.common.PreferenceHelper; import com.mendhak.gpslogger.common.Strings; +import com.mendhak.gpslogger.common.Systems; import com.mendhak.gpslogger.common.events.UploadEvents; import com.mendhak.gpslogger.common.slf4j.Logs; @@ -113,7 +114,10 @@ public Result doWork() { session.disconnect(); LOG.info("SFTP - file uploaded"); + // Notify internal listeners EventBus.getDefault().post(new UploadEvents.SFTP().succeeded()); + // Notify external listeners + Systems.sendFileUploadedBroadcast(getApplicationContext(), new String[]{fileToUpload.getAbsolutePath()}, "sftp"); return Result.success(); } else { EventBus.getDefault().post(new UploadEvents.SFTP().failed("Could not connect, unknown reasons", null)); From 907d2c41e543c810c2523bd3cd1f4e9947667b36 Mon Sep 17 00:00:00 2001 From: mendhak Date: Mon, 1 Apr 2024 20:33:14 +0100 Subject: [PATCH 57/73] Changelog for file upload broadcasts --- fastlane/metadata/android/en-US/changelogs/131.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fastlane/metadata/android/en-US/changelogs/131.txt b/fastlane/metadata/android/en-US/changelogs/131.txt index b0f3f9355..e597c865f 100644 --- a/fastlane/metadata/android/en-US/changelogs/131.txt +++ b/fastlane/metadata/android/en-US/changelogs/131.txt @@ -1,3 +1,4 @@ * OpenStreetMap option to enable or disable being prompted before logging starts -* Internal change, switching to Android WorkManager for sending and logging tasks. -* Clarified autosend description, that a dynamic file name change also results in the file being sent. \ No newline at end of file +* Internal change, switching to Android WorkManager for sending and logging tasks. Should be more reliable. +* Experimental - broadcast is sent when file upload completed, useful for automation to clean up files. +* Clarified autosend description, that a dynamic file name change also results in the file being sent. From 1f238463851b187d99f748cae142106466bf3dde Mon Sep 17 00:00:00 2001 From: mendhak Date: Mon, 1 Apr 2024 21:05:52 +0100 Subject: [PATCH 58/73] AppAuth-Android comment --- gpslogger/build.gradle | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gpslogger/build.gradle b/gpslogger/build.gradle index 8b0e2d36b..5542f53c8 100644 --- a/gpslogger/build.gradle +++ b/gpslogger/build.gradle @@ -7,9 +7,9 @@ buildscript { } } dependencies { - classpath 'com.android.tools.build:gradle:8.1.0' + classpath 'com.android.tools.build:gradle:8.1.4' - classpath 'org.jacoco:org.jacoco.core:0.7.4.201502262128' + classpath 'org.jacoco:org.jacoco.core:0.8.7' classpath "com.moowork.gradle:gradle-node-plugin:0.13" } } @@ -48,6 +48,7 @@ android { versionCode 131 versionName "131-workmgr" + // Used by AppAuth-Android manifestPlaceholders = [ appAuthRedirectScheme: 'com.mendhak.gpslogger' ] From a372dd861ee4ff1603814c3649c95330d3e6554f Mon Sep 17 00:00:00 2001 From: mendhak Date: Sat, 13 Apr 2024 20:15:20 +0100 Subject: [PATCH 59/73] Update gradle packages --- gpslogger/build.gradle | 16 ++++++++-------- gpslogger/src/main/AndroidManifest.xml | 1 + 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/gpslogger/build.gradle b/gpslogger/build.gradle index 5542f53c8..ee27dfe80 100644 --- a/gpslogger/build.gradle +++ b/gpslogger/build.gradle @@ -121,10 +121,10 @@ android { dependencies { // implementation 'androidx.legacy:legacy-support-v4:1.0.0' - implementation "androidx.activity:activity:1.3.1" - implementation "androidx.fragment:fragment:1.3.6" - implementation "androidx.preference:preference:1.1.1" - implementation "androidx.constraintlayout:constraintlayout:2.1.0" + implementation "androidx.activity:activity:1.8.2" + implementation "androidx.fragment:fragment:1.6.2" + implementation "androidx.preference:preference:1.2.1" + implementation "androidx.constraintlayout:constraintlayout:2.1.4" //Google Drive Oauth @@ -136,7 +136,7 @@ dependencies { //Debug Logging - implementation('org.slf4j:slf4j-api:1.7.6') + implementation('org.slf4j:slf4j-api:1.7.30') implementation('com.github.tony19:logback-android-classic:1.1.1-2'){ exclude group: 'com.google.android', module: 'android' } @@ -145,7 +145,7 @@ dependencies { //Android lollipop/material features including the Toolbar - implementation 'androidx.appcompat:appcompat:1.3.1' + implementation 'androidx.appcompat:appcompat:1.6.1' //Cardviews @@ -215,8 +215,8 @@ dependencies { testImplementation 'junit:junit:4.12' testImplementation 'org.mockito:mockito-core:3.10.0' testImplementation 'org.json:json:20180813' - testImplementation 'androidx.test:runner:1.4.0' - testImplementation 'androidx.test:rules:1.4.0' + testImplementation 'androidx.test:runner:1.5.2' + testImplementation 'androidx.test:rules:1.5.0' } diff --git a/gpslogger/src/main/AndroidManifest.xml b/gpslogger/src/main/AndroidManifest.xml index 9191d823a..a189a366e 100644 --- a/gpslogger/src/main/AndroidManifest.xml +++ b/gpslogger/src/main/AndroidManifest.xml @@ -35,6 +35,7 @@ android:theme="@style/AppTheme" android:name="com.mendhak.gpslogger.common.AppSettings" android:usesCleartextTraffic="true" + android:enableOnBackInvokedCallback="true" android:requestLegacyExternalStorage="true" > From 74aca9fb9f1299113f972cdb4eda9571eecf5d8c Mon Sep 17 00:00:00 2001 From: flyingOwl <3494672+flyingOwl@users.noreply.github.com> Date: Sun, 26 May 2024 15:19:18 +0200 Subject: [PATCH 60/73] Add SPD_KPH as custom URL logging parameter --- .../senders/customurl/CustomUrlManager.java | 1 + .../fragments/settings/CustomUrlFragment.java | 46 ++++++++++--------- gpslogger/src/main/res/values/strings.xml | 1 + 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java index 224479a36..bfd9ccee6 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManager.java @@ -197,6 +197,7 @@ public String getFormattedTextblock(String customLoggingUrl, replacements.put("acc", String.valueOf(sLoc.getAccuracy())); replacements.put("dir", String.valueOf(sLoc.getBearing())); replacements.put("prov", String.valueOf(sLoc.getProvider())); + replacements.put("spd_kph", String.valueOf(sLoc.getSpeed()*3.6)); replacements.put("spd", String.valueOf(sLoc.getSpeed())); replacements.put("timestamp", String.valueOf(sLoc.getTime()/1000)); diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/CustomUrlFragment.java b/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/CustomUrlFragment.java index f91460d0b..ce1ad4af5 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/CustomUrlFragment.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/CustomUrlFragment.java @@ -116,31 +116,33 @@ public boolean onPreferenceClick(Preference preference) { "{3} %DESC
" + "{4} %SAT
" + "{5} %ALT
" + - "{6} %SPD
" + - "{7} %ACC
" + - "{8} %DIR
" + - "{9} %PROV
" + - "{10} %TIMESTAMP
" + - "{11} %TIME
" + - "{12} %TIMEOFFSET
" + - "{13} %DATE
" + - "{14} %STARTTIMESTAMP
" + - "{15} %BATT
" + - "{16} %ISCHARGING
" + - "{17} %AID
" + - "{18} %SER
" + - "{19} %FILENAME
" + - "{20} %PROFILE
" + - "{21} %HDOP
" + - "{22} %VDOP
" + - "{23} %PDOP
" + - "{24} %DIST
" + - "{25} %ALL"; + "{6} %SPD_KPH
" + + "{7} %SPD
" + + "{8} %ACC
" + + "{9} %DIR
" + + "{10} %PROV
" + + "{11} %TIMESTAMP
" + + "{12} %TIME
" + + "{13} %TIMEOFFSET
" + + "{14} %DATE
" + + "{15} %STARTTIMESTAMP
" + + "{16} %BATT
" + + "{17} %ISCHARGING
" + + "{18} %AID
" + + "{19} %SER
" + + "{20} %FILENAME
" + + "{21} %PROFILE
" + + "{22} %HDOP
" + + "{23} %VDOP
" + + "{24} %PDOP
" + + "{25} %DIST
" + + "{26} %ALL"; String legend1 = MessageFormat.format(legendFormat, codeGreen, getString(R.string.txt_latitude), getString(R.string.txt_longitude), getString(R.string.txt_annotation), - getString(R.string.txt_satellites), getString(R.string.txt_altitude), getString(R.string.txt_speed), - getString(R.string.txt_accuracy), getString(R.string.txt_direction), getString(R.string.txt_provider), + getString(R.string.txt_satellites), getString(R.string.txt_altitude), getString(R.string.txt_speed_kph), + getString(R.string.txt_speed), getString(R.string.txt_accuracy), getString(R.string.txt_direction), + getString(R.string.txt_provider), getString(R.string.txt_timestamp_epoch), getString(R.string.txt_time_isoformat), getString(R.string.txt_time_with_offset_isoformat), diff --git a/gpslogger/src/main/res/values/strings.xml b/gpslogger/src/main/res/values/strings.xml index 02b4a6860..ae8eca117 100644 --- a/gpslogger/src/main/res/values/strings.xml +++ b/gpslogger/src/main/res/values/strings.xml @@ -26,6 +26,7 @@ Latitude: Longitude: Altitude: + Speed (in km/h): Speed: Direction: Satellites: From 943a8943e1cdf4ae3e6675438994eb792a19886a Mon Sep 17 00:00:00 2001 From: flyingOwl <3494672+flyingOwl@users.noreply.github.com> Date: Sun, 26 May 2024 15:28:53 +0200 Subject: [PATCH 61/73] Add SPD_KPH to unit tests Modify speed value so that a conversion to km/h results in an integer value --- .../senders/customurl/CustomUrlManagerTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gpslogger/src/test/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManagerTest.java b/gpslogger/src/test/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManagerTest.java index b9bc5b727..26d197866 100644 --- a/gpslogger/src/test/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManagerTest.java +++ b/gpslogger/src/test/java/com/mendhak/gpslogger/senders/customurl/CustomUrlManagerTest.java @@ -27,15 +27,15 @@ public void getFormattedUrl_WhenPlaceholders_ValuesSubstituted() throws Exceptio .withAltitude(45) .withAccuracy(8) .withBearing(359) - .withSpeed(9001) + .withSpeed(9005) .withTime(1457205869949l) .build(); CustomUrlManager manager = new CustomUrlManager(null); - String expected = "http://192.168.1.65:8000/test?lat=12.193&lon=19.111&sat=9&desc=blah&alt=45.0&acc=8.0&dir=359.0&prov=MOCK&spd=9001.0&time=2016-03-05T19:24:29.949Z&battery=91.0&androidId=22&serial=SRS11&activity="; - String urlTemplate = "http://192.168.1.65:8000/test?lat=%LAT&lon=%LON&sat=%SAT&desc=%DESC&alt=%ALT&acc=%ACC&dir=%DIR&prov=%PROV&spd=%SPD&time=%TIME&battery=%BATT&androidId=%AID&serial=%SER&activity=%act"; + String expected = "http://192.168.1.65:8000/test?lat=12.193&lon=19.111&sat=9&desc=blah&alt=45.0&acc=8.0&dir=359.0&prov=MOCK&spd_kph=32418.0&spd=9005.0&time=2016-03-05T19:24:29.949Z&battery=91.0&androidId=22&serial=SRS11&activity="; + String urlTemplate = "http://192.168.1.65:8000/test?lat=%LAT&lon=%LON&sat=%SAT&desc=%DESC&alt=%ALT&acc=%ACC&dir=%DIR&prov=%PROV&spd_kph=%SPD_KPH&spd=%SPD&time=%TIME&battery=%BATT&androidId=%AID&serial=%SER&activity=%act"; assertThat("Placeholders are substituted", manager.getFormattedTextblock(urlTemplate, new SerializableLocation(loc), "blah", "22", 91, false, "SRS11", 0, "", "", @@ -289,7 +289,7 @@ public void getFormattedUrl_WhenALLParameters_AllKeyValuesAddedDirectly() throws .putExtra(BundleConstants.VDOP, "19").withTime(1457205869949l).build(); CustomUrlManager manager = new CustomUrlManager(null); String expected = "http://192.168.1.65:8000/test?lat=12.193&lon=19.456&sat=0&desc=&alt=0.0" + - "&acc=0.0&dir=0.0&prov=MOCK&spd=0.0×tamp=1457205869" + + "&acc=0.0&dir=0.0&prov=MOCK&spd_kph=0.0&spd=0.0×tamp=1457205869" + "&timeoffset=2016-03-05T21:24:29.949%2B02:00&time=2016-03-05T19:24:29.949Z" + "&starttimestamp=1495884681&date=2016-03-05&batt=0.0&ischarging=false&aid=&ser=" + "&act=&filename=20170527abc&profile=Default+Profile&hdop=&vdop=19&pdop=&dist=0&"; @@ -307,7 +307,7 @@ public void getFormattedUrl_WhenALLParametersInBody_AllKeyValuesAddedDirectly() .putExtra(BundleConstants.VDOP, "19").withTime(1457205869949l).build(); CustomUrlManager manager = new CustomUrlManager(null); String expected = "lat=12.193&lon=19.456&sat=0&desc=&alt=0.0&acc=0.0&dir=0.0&prov=MOCK" + - "&spd=0.0×tamp=1457205869&timeoffset=2016-03-05T21:24:29.949%2B02:00" + + "&spd_kph=0.0&spd=0.0×tamp=1457205869&timeoffset=2016-03-05T21:24:29.949%2B02:00" + "&time=2016-03-05T19:24:29.949Z&starttimestamp=1495884681&date=2016-03-05" + "&batt=0.0&ischarging=false&aid=&ser=&act=&filename=20170527abc" + "&profile=Default+Profile&hdop=&vdop=19&pdop=&dist=0&"; From c62b680e78077d7ee8c934fbd4f742a51cc7a74f Mon Sep 17 00:00:00 2001 From: mendhak Date: Sun, 26 May 2024 15:37:42 +0100 Subject: [PATCH 62/73] New error notification channel for critical errors such as lack of permissions or unable to write file. Issue #1053 Issue #1138 --- build.gradle | 1 + .../mendhak/gpslogger/GpsLoggingService.java | 38 +++++------------- .../mendhak/gpslogger/common/AppSettings.java | 29 ++++++++++++++ .../common/NotificationChannelNames.java | 8 ++++ .../com/mendhak/gpslogger/common/Systems.java | 40 +++++++++++++++++++ 5 files changed, 88 insertions(+), 28 deletions(-) create mode 100644 build.gradle create mode 100644 gpslogger/src/main/java/com/mendhak/gpslogger/common/NotificationChannelNames.java diff --git a/build.gradle b/build.gradle new file mode 100644 index 000000000..495c5038e --- /dev/null +++ b/build.gradle @@ -0,0 +1 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java b/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java index add8fe3f9..5581ac7f3 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java @@ -55,7 +55,6 @@ @SuppressLint("MissingPermission") public class GpsLoggingService extends Service { private static NotificationManager notificationManager; - private static int NOTIFICATION_ID = 8675309; private final IBinder binder = new GpsLoggingBinder(); AlarmManager nextPointAlarmManager; private NotificationCompat.Builder nfc; @@ -89,10 +88,10 @@ public IBinder onBind(Intent arg0) { public void onCreate() { try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - startForeground(NOTIFICATION_ID, getNotification(), ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION); + startForeground(NotificationChannelNames.GPSLOGGER_DEFAULT_NOTIFICATION_ID, getNotification(), ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION); } else { - startForeground(NOTIFICATION_ID, getNotification()); + startForeground(NotificationChannelNames.GPSLOGGER_DEFAULT_NOTIFICATION_ID, getNotification()); } } catch (Exception ex) { LOG.error("Could not start GPSLoggingService in foreground. ", ex); @@ -126,10 +125,10 @@ public int onStartCommand(Intent intent, int flags, int startId) { super.onStartCommand(intent, flags, startId); try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - startForeground(NOTIFICATION_ID, getNotification(), ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION); + startForeground(NotificationChannelNames.GPSLOGGER_DEFAULT_NOTIFICATION_ID, getNotification(), ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION); } else { - startForeground(NOTIFICATION_ID, getNotification()); + startForeground(NotificationChannelNames.GPSLOGGER_DEFAULT_NOTIFICATION_ID, getNotification()); } } catch (Exception ex) { LOG.error("Could not start GPSLoggingService in foreground. ", ex); @@ -179,8 +178,7 @@ private void handleIntent(Intent intent) { if(!Systems.locationPermissionsGranted(this)){ LOG.error("User has not granted permission to access location services. Will not continue!"); - stopLogging(); - stopSelf(); + Systems.showErrorNotification(this, getString(R.string.gpslogger_permissions_permanently_denied)); return; } @@ -398,10 +396,10 @@ protected void startLogging() { try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - startForeground(NOTIFICATION_ID, getNotification(), ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION); + startForeground(NotificationChannelNames.GPSLOGGER_DEFAULT_NOTIFICATION_ID, getNotification(), ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION); } else { - startForeground(NOTIFICATION_ID, getNotification()); + startForeground(NotificationChannelNames.GPSLOGGER_DEFAULT_NOTIFICATION_ID, getNotification()); } } catch (Exception ex) { LOG.error("Could not start GPSLoggingService in foreground. ", ex); @@ -529,21 +527,7 @@ private Notification getNotification() { if (nfc == null) { - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { - NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - - NotificationChannel channel = new NotificationChannel("gpslogger", getString(R.string.app_name), NotificationManager.IMPORTANCE_DEFAULT); - channel.enableLights(false); - channel.enableVibration(false); - channel.setSound(null,null); - channel.setLockscreenVisibility(preferenceHelper.shouldHideNotificationFromLockScreen() ? Notification.VISIBILITY_PRIVATE : Notification.VISIBILITY_PUBLIC); - - channel.setShowBadge(true); - manager.createNotificationChannel(channel); - - } - - nfc = new NotificationCompat.Builder(getApplicationContext(),"gpslogger") + nfc = new NotificationCompat.Builder(getApplicationContext(), NotificationChannelNames.GPSLOGGER_DEFAULT) .setSmallIcon(R.drawable.notification) .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.gpsloggericon3)) .setPriority( preferenceHelper.shouldHideNotificationFromStatusBar() ? NotificationCompat.PRIORITY_MIN : NotificationCompat.PRIORITY_LOW) @@ -566,22 +550,20 @@ private Notification getNotification() { } } - - nfc.setContentTitle(contentTitle); nfc.setContentText(contentText); nfc.setStyle(new NotificationCompat.BigTextStyle().bigText(contentText).setBigContentTitle(contentTitle)); nfc.setWhen(notificationTime); //notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); - //notificationManager.notify(NOTIFICATION_ID, nfc.build()); + //notificationManager.notify(NotificationChannelNames.GPSLOGGER_DEFAULT_ID, nfc.build()); return nfc.build(); } private void showNotification(){ Notification notif = getNotification(); notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); - notificationManager.notify(NOTIFICATION_ID, notif); + notificationManager.notify(NotificationChannelNames.GPSLOGGER_DEFAULT_NOTIFICATION_ID, notif); } @SuppressWarnings("ResourceType") diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/common/AppSettings.java b/gpslogger/src/main/java/com/mendhak/gpslogger/common/AppSettings.java index b3ebd6266..1575a3d9e 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/common/AppSettings.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/common/AppSettings.java @@ -20,9 +20,14 @@ package com.mendhak.gpslogger.common; import android.app.Application; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.content.Context; import com.mendhak.gpslogger.BuildConfig; +import com.mendhak.gpslogger.R; import com.mendhak.gpslogger.common.slf4j.Logs; import de.greenrobot.event.EventBus; import org.slf4j.Logger; @@ -36,6 +41,7 @@ public class AppSettings extends Application { @Override public void onCreate() { + Systems.setAppTheme(PreferenceHelper.getInstance().getAppThemeSetting()); super.onCreate(); @@ -48,7 +54,30 @@ public void onCreate() { EventBus.builder().logNoSubscriberMessages(false).sendNoSubscriberEvent(false).installDefaultEventBus(); LOG.debug("EventBus configured"); + createNotificationChannels(); + } + + private void createNotificationChannels() { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + + NotificationChannel channel = new NotificationChannel(NotificationChannelNames.GPSLOGGER_DEFAULT, getString(R.string.app_name), NotificationManager.IMPORTANCE_DEFAULT); + channel.enableLights(false); + channel.enableVibration(false); + channel.setSound(null,null); + channel.setLockscreenVisibility(PreferenceHelper.getInstance().shouldHideNotificationFromLockScreen() ? Notification.VISIBILITY_PRIVATE : Notification.VISIBILITY_PUBLIC); + + channel.setShowBadge(true); + manager.createNotificationChannel(channel); + + NotificationChannel channelErrors = new NotificationChannel(NotificationChannelNames.GPSLOGGER_ERRORS, getString(R.string.error), NotificationManager.IMPORTANCE_HIGH); + channelErrors.enableLights(true); + channelErrors.enableVibration(true); + channelErrors.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); + channelErrors.setShowBadge(true); + manager.createNotificationChannel(channelErrors); + } } diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/common/NotificationChannelNames.java b/gpslogger/src/main/java/com/mendhak/gpslogger/common/NotificationChannelNames.java new file mode 100644 index 000000000..8ed069764 --- /dev/null +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/common/NotificationChannelNames.java @@ -0,0 +1,8 @@ +package com.mendhak.gpslogger.common; + +public class NotificationChannelNames { + public static final String GPSLOGGER_DEFAULT = "gpslogger"; + public static final int GPSLOGGER_DEFAULT_NOTIFICATION_ID = 8675309; + public static final String GPSLOGGER_ERRORS = "gpslogger_errors"; + public static final int GPSLOGGER_ERRORS_NOTIFICATION_ID = 32202; +} diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java b/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java index 930f7901d..9e90758c5 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java @@ -22,6 +22,8 @@ import android.Manifest; import android.annotation.TargetApi; +import android.app.NotificationManager; +import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -30,14 +32,19 @@ import android.content.pm.Signature; import android.content.res.Configuration; import android.content.res.Resources; +import android.graphics.BitmapFactory; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.BatteryManager; import android.os.Build; import android.os.PowerManager; import android.provider.Settings; +import android.text.Html; + import androidx.appcompat.app.AppCompatDelegate; +import androidx.core.app.NotificationCompat; import androidx.core.content.ContextCompat; +import androidx.core.text.HtmlCompat; import androidx.fragment.app.FragmentActivity; import androidx.work.BackoffPolicy; import androidx.work.Constraints; @@ -47,6 +54,8 @@ import androidx.work.OneTimeWorkRequest; import androidx.work.WorkManager; +import com.mendhak.gpslogger.GpsMainActivity; +import com.mendhak.gpslogger.R; import com.mendhak.gpslogger.common.slf4j.Logs; import org.slf4j.Logger; @@ -281,4 +290,35 @@ public static void sendFileUploadedBroadcast(Context context, String[] filePaths sendIntent.putExtra("sendertype", senderType); context.sendBroadcast(sendIntent); } + + /** + * Show an error notification with a warning emoji ⚠️, this is only used for important errors worth notifying the user for. + * Such as location permissions missing, unable to write to storage. + * @param context The application context, so that the notification service can be accessed. + * @param message A single line message to show in the notification. + */ + public static void showErrorNotification(Context context, String message){ + LOG.debug("Showing fatal notification"); + + Intent contentIntent = new Intent(context, GpsMainActivity.class); + PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT); + + NotificationCompat.Builder nfc = new NotificationCompat.Builder(context.getApplicationContext(), NotificationChannelNames.GPSLOGGER_ERRORS) + .setSmallIcon(android.R.drawable.stat_notify_error) + .setLargeIcon(BitmapFactory.decodeResource(context.getResources(), android.R.drawable.stat_notify_error)) + .setPriority(NotificationCompat.PRIORITY_HIGH) + .setCategory(NotificationCompat.CATEGORY_ERROR) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setContentTitle(context.getString(R.string.error)) + .setContentText(HtmlCompat.fromHtml(message, HtmlCompat.FROM_HTML_MODE_COMPACT).toString()) + .setStyle(new NotificationCompat.BigTextStyle().bigText(Html.fromHtml(message).toString()).setBigContentTitle(context.getString(R.string.error))) + .setOngoing(false) + .setOnlyAlertOnce(true) + .setContentIntent(pendingIntent); + + + NotificationManager notificationManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE); + notificationManager.notify(NotificationChannelNames.GPSLOGGER_ERRORS_NOTIFICATION_ID, nfc.build()); + + } } From da36634e2f819a68d3b461d7adc478f7262d8f13 Mon Sep 17 00:00:00 2001 From: mendhak Date: Sun, 26 May 2024 16:21:17 +0100 Subject: [PATCH 63/73] Rearrange SPD and SPD_KPH --- .../ui/fragments/settings/CustomUrlFragment.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/CustomUrlFragment.java b/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/CustomUrlFragment.java index ce1ad4af5..c79af7318 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/CustomUrlFragment.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/CustomUrlFragment.java @@ -116,8 +116,8 @@ public boolean onPreferenceClick(Preference preference) { "{3} %DESC
" + "{4} %SAT
" + "{5} %ALT
" + - "{6} %SPD_KPH
" + - "{7} %SPD
" + + "{6} %SPD
" + + "{7} %SPD_KPH
" + "{8} %ACC
" + "{9} %DIR
" + "{10} %PROV
" + @@ -140,8 +140,8 @@ public boolean onPreferenceClick(Preference preference) { String legend1 = MessageFormat.format(legendFormat, codeGreen, getString(R.string.txt_latitude), getString(R.string.txt_longitude), getString(R.string.txt_annotation), - getString(R.string.txt_satellites), getString(R.string.txt_altitude), getString(R.string.txt_speed_kph), - getString(R.string.txt_speed), getString(R.string.txt_accuracy), getString(R.string.txt_direction), + getString(R.string.txt_satellites), getString(R.string.txt_altitude), getString(R.string.txt_speed), + getString(R.string.txt_speed_kph), getString(R.string.txt_accuracy), getString(R.string.txt_direction), getString(R.string.txt_provider), getString(R.string.txt_timestamp_epoch), getString(R.string.txt_time_isoformat), From 95fa61a65e157ebc440e147f055349f743449f3c Mon Sep 17 00:00:00 2001 From: mendhak Date: Sun, 26 May 2024 22:13:59 +0100 Subject: [PATCH 64/73] Stabilize the app when permissions have been revoked. Moved the start service to only after the permission check is complete. In the stop service, only stop the service if notifications are enabled, due to a weird scenario where the service might start and immediately end due to lack of notifications, resulting in an app crash. How did we end up like this. Issue #1138 Issue #1053 --- gpslogger/src/main/AndroidManifest.xml | 2 ++ .../mendhak/gpslogger/GpsMainActivity.java | 26 ++++++++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/gpslogger/src/main/AndroidManifest.xml b/gpslogger/src/main/AndroidManifest.xml index a189a366e..bcf749f78 100644 --- a/gpslogger/src/main/AndroidManifest.xml +++ b/gpslogger/src/main/AndroidManifest.xml @@ -27,6 +27,8 @@ + + = Build.VERSION_CODES.TIRAMISU) { + permissions.add(Manifest.permission.POST_NOTIFICATIONS); + } if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) { // Only on Android 10 (Q), the permission dialog can include an 'Allow all the time' @@ -1501,6 +1507,16 @@ private void startAndBindService() { * Stops the service if it isn't logging. Also unbinds. */ private void stopAndUnbindServiceIfRequired() { + if(!NotificationManagerCompat.from(this).areNotificationsEnabled()) { + // Alright. Why is this needed. + // If the notification permission has been revoked or not granted for whatever reason. + // When the application opens, the service starts, then stops right away. + // Android requires a notification to be shown for a foreground service within 5 seconds. + // So the application crashes and comes back repeatedly. Very weird. + // The answer - if notifications are disabled, don't unbind the service. It will stop on its own. + // Might be related: https://stackoverflow.com/questions/73067939/start-foreground-service-after-notification-permission-was-disabled-causes-crash + return; + } if (session.isBoundToService()) { try { @@ -1514,6 +1530,10 @@ private void stopAndUnbindServiceIfRequired() { if (!session.isStarted()) { LOG.debug("Stopping the service"); try { + // Stop service crashes if the intent is null. lol + if(serviceIntent == null){ + serviceIntent = new Intent(this, GpsLoggingService.class); + } stopService(serviceIntent); } catch (Exception e) { LOG.error("Could not stop the service", e); From 972c06e10478813d32d516ca7f7a07fb70751789 Mon Sep 17 00:00:00 2001 From: mendhak Date: Mon, 27 May 2024 07:56:45 +0100 Subject: [PATCH 65/73] Remove the big icon because it was only visible in dark mode. --- .../src/main/java/com/mendhak/gpslogger/common/Systems.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java b/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java index 9e90758c5..8b319f674 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java @@ -304,8 +304,8 @@ public static void showErrorNotification(Context context, String message){ PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT); NotificationCompat.Builder nfc = new NotificationCompat.Builder(context.getApplicationContext(), NotificationChannelNames.GPSLOGGER_ERRORS) - .setSmallIcon(android.R.drawable.stat_notify_error) - .setLargeIcon(BitmapFactory.decodeResource(context.getResources(), android.R.drawable.stat_notify_error)) + .setSmallIcon(android.R.drawable.stat_sys_warning) + //.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), android.R.drawable.stat_sys_warning)) .setPriority(NotificationCompat.PRIORITY_HIGH) .setCategory(NotificationCompat.CATEGORY_ERROR) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) From 708b60bbd0f73c8c2184e16d371b1e4ddec63e11 Mon Sep 17 00:00:00 2001 From: mendhak Date: Mon, 27 May 2024 11:22:24 +0100 Subject: [PATCH 66/73] Show a notification if file write failure occurs. Continues logging anyway, except for NMEA, which stops logging. This is because NMEA has a high frequency of writes. Issue #1138 #1053 --- .../com/mendhak/gpslogger/GpsLoggingService.java | 9 +++++++++ .../gpslogger/common/events/CommandEvents.java | 16 ++++++++++++++++ .../loggers/geojson/GeoJSONWriterPoints.java | 7 +++++-- .../gpslogger/loggers/gpx/Gpx10FileLogger.java | 9 +++++++-- .../gpslogger/loggers/kml/Kml22FileLogger.java | 9 +++++++-- .../gpslogger/loggers/nmea/NmeaFileLogger.java | 14 ++++++++++++-- 6 files changed, 56 insertions(+), 8 deletions(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java b/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java index 5581ac7f3..be5e5614f 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java @@ -1074,6 +1074,7 @@ private void writeToFile(Location loc) { } catch(Exception e){ LOG.error(getString(R.string.could_not_write_to_file), e); + Systems.showErrorNotification(this, getString(R.string.could_not_write_to_file)); } session.clearDescription(); @@ -1174,6 +1175,14 @@ public void onEvent(CommandEvents.LogOnce logOnce){ } + @EventBusHook + public void onEvent(CommandEvents.FileWriteFailure writeFailure){ + Systems.showErrorNotification(this, getString(R.string.could_not_write_to_file)); + if(writeFailure.stopLoggingDueToNMEA){ + LOG.error("Could not write to NMEA file, stopping logging due to high frequency of write failures"); + stopLogging(); + } + } @EventBusHook public void onEvent(ProfileEvents.SwitchToProfile switchToProfileEvent){ diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/common/events/CommandEvents.java b/gpslogger/src/main/java/com/mendhak/gpslogger/common/events/CommandEvents.java index c38d2729c..4e0ea73ee 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/common/events/CommandEvents.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/common/events/CommandEvents.java @@ -72,4 +72,20 @@ public Annotate(String annotation) { */ public static class LogOnce { } + + /** + * Used to indicate that the file write failed. + * The intention is to then notify the user of the failure since it represents data loss. + * Pass stopLogging, if true, ask the logging to stop. Use this for NMEA which is very high frequency. + */ + public static class FileWriteFailure { + public boolean stopLoggingDueToNMEA; + + public FileWriteFailure(){ + this.stopLoggingDueToNMEA = false; + } + public FileWriteFailure(boolean stopLoggingDueToNMEA) { + this.stopLoggingDueToNMEA = stopLoggingDueToNMEA; + } + } } diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/geojson/GeoJSONWriterPoints.java b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/geojson/GeoJSONWriterPoints.java index cd28eb0f3..1dfac8ac7 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/geojson/GeoJSONWriterPoints.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/geojson/GeoJSONWriterPoints.java @@ -4,6 +4,7 @@ import androidx.annotation.NonNull; import com.mendhak.gpslogger.common.Strings; +import com.mendhak.gpslogger.common.events.CommandEvents; import com.mendhak.gpslogger.common.slf4j.Logs; import com.mendhak.gpslogger.loggers.Files; @@ -13,6 +14,8 @@ import java.io.IOException; import java.io.RandomAccessFile; +import de.greenrobot.event.EventBus; + /** * Created by clemens on 10.05.17. */ @@ -66,8 +69,8 @@ public void run() { raf.close(); } } catch (IOException e) { - e.printStackTrace(); - LOG.error("GeoJSONWriterPoints", e); + EventBus.getDefault().post(new CommandEvents.FileWriteFailure()); + LOG.error("Failed to write to GeoJSON file", e); } } diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/gpx/Gpx10FileLogger.java b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/gpx/Gpx10FileLogger.java index 2cd6a1452..36af142d3 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/gpx/Gpx10FileLogger.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/gpx/Gpx10FileLogger.java @@ -26,6 +26,7 @@ import com.mendhak.gpslogger.common.PreferenceHelper; import com.mendhak.gpslogger.common.RejectionHandler; import com.mendhak.gpslogger.common.Strings; +import com.mendhak.gpslogger.common.events.CommandEvents; import com.mendhak.gpslogger.common.slf4j.Logs; import com.mendhak.gpslogger.loggers.FileLogger; import com.mendhak.gpslogger.loggers.Files; @@ -37,6 +38,8 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import de.greenrobot.event.EventBus; + public class Gpx10FileLogger implements FileLogger { protected final static Object lock = new Object(); @@ -160,7 +163,8 @@ public void run() { LOG.debug("Finished annotation to GPX10 File"); } catch (Exception e) { - LOG.error("Gpx10FileLogger.annotate", e); + EventBus.getDefault().post(new CommandEvents.FileWriteFailure()); + LOG.error("Error annotating GPX file", e); } } @@ -241,7 +245,8 @@ public void run() { LOG.debug("Finished writing to GPX10 file"); } catch (Exception e) { - LOG.error("Gpx10FileLogger.write", e); + EventBus.getDefault().post(new CommandEvents.FileWriteFailure()); + LOG.error("Error writing to GPX file", e); } } diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/kml/Kml22FileLogger.java b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/kml/Kml22FileLogger.java index 87e01242e..b9952a83c 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/kml/Kml22FileLogger.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/kml/Kml22FileLogger.java @@ -24,6 +24,7 @@ import com.mendhak.gpslogger.common.PreferenceHelper; import com.mendhak.gpslogger.common.RejectionHandler; import com.mendhak.gpslogger.common.Strings; +import com.mendhak.gpslogger.common.events.CommandEvents; import com.mendhak.gpslogger.common.slf4j.Logs; import com.mendhak.gpslogger.loggers.FileLogger; import com.mendhak.gpslogger.loggers.Files; @@ -36,6 +37,8 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import de.greenrobot.event.EventBus; + public class Kml22FileLogger implements FileLogger { protected final static Object lock = new Object(); private final static ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS, @@ -117,7 +120,8 @@ public void run() { } } catch (Exception e) { - LOG.error("Kml22FileLogger.annotate", e); + EventBus.getDefault().post(new CommandEvents.FileWriteFailure()); + LOG.error("Error writing KML annotation", e); } } @@ -222,7 +226,8 @@ public void run() { } } catch (Exception e) { - LOG.error("Kml22FileLogger.write", e); + EventBus.getDefault().post(new CommandEvents.FileWriteFailure()); + LOG.error("Error writing KML file", e); } } } diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/nmea/NmeaFileLogger.java b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/nmea/NmeaFileLogger.java index bee4d5ad5..5653fcfcd 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/nmea/NmeaFileLogger.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/nmea/NmeaFileLogger.java @@ -23,8 +23,12 @@ import com.mendhak.gpslogger.common.RejectionHandler; import com.mendhak.gpslogger.common.Session; import com.mendhak.gpslogger.common.Strings; +import com.mendhak.gpslogger.common.events.CommandEvents; +import com.mendhak.gpslogger.common.slf4j.Logs; import com.mendhak.gpslogger.loggers.Files; +import org.slf4j.Logger; + import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; @@ -33,9 +37,12 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import de.greenrobot.event.EventBus; + public class NmeaFileLogger { protected final static Object lock = new Object(); + private static final Logger LOG = Logs.of(NmeaFileLogger.class); String fileName; private final static ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS, new LinkedBlockingQueue(10), new RejectionHandler()); @@ -60,7 +67,8 @@ public void write(long timestamp, String nmeaSentence) { try { nmeaFile.createNewFile(); } catch (IOException e) { - + LOG.error("Error creating NMEA file", e); + EventBus.getDefault().post(new CommandEvents.FileWriteFailure(true)); } } @@ -71,6 +79,7 @@ public void write(long timestamp, String nmeaSentence) { class NmeaWriteHandler implements Runnable { + private static final Logger LOG = Logs.of(NmeaWriteHandler.class); File gpxFile; String nmeaSentence; @@ -92,7 +101,8 @@ public void run() { Files.addToMediaDatabase(gpxFile, "text/plain"); } catch (IOException e) { - + LOG.error("Error writing NMEA sentence", e); + EventBus.getDefault().post(new CommandEvents.FileWriteFailure(true)); } } From a93d29ff0cad0fdc753e199dfaf29ce37330ea24 Mon Sep 17 00:00:00 2001 From: mendhak Date: Mon, 27 May 2024 12:51:38 +0100 Subject: [PATCH 67/73] Update links and similar apps --- assets/text/faq/faq13-difference-other-apps.md | 4 +++- assets/text/faq/faq17-other-uses.md | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/assets/text/faq/faq13-difference-other-apps.md b/assets/text/faq/faq13-difference-other-apps.md index 051c73615..001c33b69 100644 --- a/assets/text/faq/faq13-difference-other-apps.md +++ b/assets/text/faq/faq13-difference-other-apps.md @@ -1,6 +1,8 @@ ## How is this different from other logging apps? -It's meant to be more battery efficient. A lot of other apps, such as [OpenTracks](https://github.com/OpenTracksApp/OpenTracks), usually go with the assumption that you have a data connection available and your routes won't be very long. They use CPU wakelocks and log points extremely frequently with high accuracy. The aim of GPSLogger is to log points and stay quiet. +It's meant to be more battery efficient. A lot of other apps, such as [OpenTracks](https://github.com/OpenTracksApp/OpenTracks), usually go with the assumption that you have a data connection available and your routes won't be very long. +They use CPU wakelocks and log points extremely frequently with high accuracy. The aim of GPSLogger is to log points and stay quiet. To put it another way, OpenTracks or similar are better suited for runs; GPSLogger is suited for days out, hiking, photography. +[BasicAirData GPSLogger](https://play.google.com/store/apps/details?id=eu.basicairdata.graziano.gpslogger&hl=en&gl=US), [Ultra GPS Logger](https://play.google.com/store/apps/details?id=com.flashlight.lite.gps.logger&hl=en&gl=US) have similar names but aren't associated with this project. \ No newline at end of file diff --git a/assets/text/faq/faq17-other-uses.md b/assets/text/faq/faq17-other-uses.md index 3e2738153..d2facc2c3 100644 --- a/assets/text/faq/faq17-other-uses.md +++ b/assets/text/faq/faq17-other-uses.md @@ -3,13 +3,13 @@ The GPS files produced by this app are generally used for processing *other* things. -A common use case is to geotag photos. Many cameras, especially SLRs, don't have built-in GPS. After a day (or days) out of photography, you may have hundreds of photos that need to be geotagged so that their locations can appear properly when used elsewhere. +A common use case is to geotag photos. Many cameras, especially SLRs, don't have built-in GPS. After a day (or days) out of photography, you may have hundreds of photos that need to be geotagged so that their locations can appear properly when used elsewhere. I have had success with: * [GeoSetter](http://www.geosetter.de/en/) - GUI, comprehensive options with map display -* [ExifTool](http://askubuntu.com/questions/599395/how-can-i-batch-tag-several-hundred-photos-with-separately-recorded-gps-data) - command line, lots of options -* Lightroom's map module - very basic and limited +* [digiKam](https://www.digikam.org/) - Open Source photo management. Geotagging and geocoding are tools within this software +* [ExifTool](http://askubuntu.com/questions/599395/how-can-i-batch-tag-several-hundred-photos-with-separately-recorded-gps-data) - command line, lots of options There are of course other uses of the produced files, these are a few I've seen over the years; it's usually a combination of a log file produced from GPSLogger with a secondary software to process the files. From f0d886ae074bbba7eceb1ae97256280a56337fa1 Mon Sep 17 00:00:00 2001 From: mendhak Date: Mon, 27 May 2024 18:53:25 +0100 Subject: [PATCH 68/73] When Custom URL requests fail, show an error channel notification. Issue #1079 --- .../java/com/mendhak/gpslogger/GpsLoggingService.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java b/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java index be5e5614f..cbd37502b 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java @@ -36,6 +36,7 @@ import com.mendhak.gpslogger.common.events.CommandEvents; import com.mendhak.gpslogger.common.events.ProfileEvents; import com.mendhak.gpslogger.common.events.ServiceEvents; +import com.mendhak.gpslogger.common.events.UploadEvents; import com.mendhak.gpslogger.common.network.ConscryptProviderInstaller; import com.mendhak.gpslogger.common.slf4j.Logs; import com.mendhak.gpslogger.common.slf4j.SessionLogcatAppender; @@ -1184,6 +1185,16 @@ public void onEvent(CommandEvents.FileWriteFailure writeFailure){ } } + @EventBusHook + public void onEvent(UploadEvents.CustomUrl upload){ + if(!upload.success){ + Systems.showErrorNotification(this, getString(R.string.log_customurl_setup_title) + + "-" + + getString(R.string.upload_failure)); + + } + } + @EventBusHook public void onEvent(ProfileEvents.SwitchToProfile switchToProfileEvent){ try { From 22d1ece59bd8428a57d1e732d952375fc80aaf1e Mon Sep 17 00:00:00 2001 From: mendhak Date: Mon, 27 May 2024 20:11:50 +0100 Subject: [PATCH 69/73] Revert "When Custom URL requests fail, show an error channel notification." This reverts commit f0d886ae074bbba7eceb1ae97256280a56337fa1. --- .../java/com/mendhak/gpslogger/GpsLoggingService.java | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java b/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java index cbd37502b..be5e5614f 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java @@ -36,7 +36,6 @@ import com.mendhak.gpslogger.common.events.CommandEvents; import com.mendhak.gpslogger.common.events.ProfileEvents; import com.mendhak.gpslogger.common.events.ServiceEvents; -import com.mendhak.gpslogger.common.events.UploadEvents; import com.mendhak.gpslogger.common.network.ConscryptProviderInstaller; import com.mendhak.gpslogger.common.slf4j.Logs; import com.mendhak.gpslogger.common.slf4j.SessionLogcatAppender; @@ -1185,16 +1184,6 @@ public void onEvent(CommandEvents.FileWriteFailure writeFailure){ } } - @EventBusHook - public void onEvent(UploadEvents.CustomUrl upload){ - if(!upload.success){ - Systems.showErrorNotification(this, getString(R.string.log_customurl_setup_title) - + "-" - + getString(R.string.upload_failure)); - - } - } - @EventBusHook public void onEvent(ProfileEvents.SwitchToProfile switchToProfileEvent){ try { From b8a36de5bcc8bb0497588d118bbc96bcdeb11a51 Mon Sep 17 00:00:00 2001 From: mendhak Date: Mon, 27 May 2024 20:28:14 +0100 Subject: [PATCH 70/73] Update changelog --- fastlane/metadata/android/en-US/changelogs/131.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fastlane/metadata/android/en-US/changelogs/131.txt b/fastlane/metadata/android/en-US/changelogs/131.txt index e597c865f..1e6b69ced 100644 --- a/fastlane/metadata/android/en-US/changelogs/131.txt +++ b/fastlane/metadata/android/en-US/changelogs/131.txt @@ -1,4 +1,6 @@ -* OpenStreetMap option to enable or disable being prompted before logging starts -* Internal change, switching to Android WorkManager for sending and logging tasks. Should be more reliable. +* OpenStreetMap option to be prompted before logging starts +* Switching to Android WorkManager for sending and logging tasks. Should be more reliable. * Experimental - broadcast is sent when file upload completed, useful for automation to clean up files. * Clarified autosend description, that a dynamic file name change also results in the file being sent. +* Error notifications if permissions have been revoked or file writes fail. +* New Custom URL parameter, %SPD_KPH \ No newline at end of file From cff23f006983e0fe2be509e223498e57044ec503 Mon Sep 17 00:00:00 2001 From: mendhak Date: Mon, 27 May 2024 20:28:38 +0100 Subject: [PATCH 71/73] Version update --- gpslogger/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpslogger/build.gradle b/gpslogger/build.gradle index ee27dfe80..6a92746f5 100644 --- a/gpslogger/build.gradle +++ b/gpslogger/build.gradle @@ -46,7 +46,7 @@ android { targetSdkVersion 30 compileSdk 34 versionCode 131 - versionName "131-workmgr" + versionName "131-rc2" // Used by AppAuth-Android manifestPlaceholders = [ From cd32b4850fb5a20bcbccd5008c036e9640ba1cf4 Mon Sep 17 00:00:00 2001 From: mendhak Date: Tue, 28 May 2024 08:25:37 +0100 Subject: [PATCH 72/73] Set notification to auto close when clicking. Also when recovering from unexpected stop, check permissions before restarting logging --- .../main/java/com/mendhak/gpslogger/GpsLoggingService.java | 7 +++++-- .../main/java/com/mendhak/gpslogger/common/Systems.java | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java b/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java index be5e5614f..5e4550464 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java @@ -135,8 +135,11 @@ public int onStartCommand(Intent intent, int flags, int startId) { } if(session.isStarted() && gpsLocationListener == null && towerLocationListener == null && passiveLocationListener == null) { - LOG.warn("App might be recovering from an unexpected stop. Starting logging again."); - startLogging(); + if(Systems.hasUserGrantedAllNecessaryPermissions(this)){ + LOG.warn("App might be recovering from an unexpected stop. Starting logging again."); + startLogging(); + } + } handleIntent(intent); diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java b/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java index 8b319f674..9bbb42f0b 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java @@ -314,6 +314,7 @@ public static void showErrorNotification(Context context, String message){ .setStyle(new NotificationCompat.BigTextStyle().bigText(Html.fromHtml(message).toString()).setBigContentTitle(context.getString(R.string.error))) .setOngoing(false) .setOnlyAlertOnce(true) + .setAutoCancel(true) .setContentIntent(pendingIntent); From 41ca664cb8b689feef65d7db9993a7a728acc1ed Mon Sep 17 00:00:00 2001 From: mendhak Date: Sun, 2 Jun 2024 14:46:21 +0100 Subject: [PATCH 73/73] Rearrange releaes notes --- fastlane/metadata/android/en-US/changelogs/131.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fastlane/metadata/android/en-US/changelogs/131.txt b/fastlane/metadata/android/en-US/changelogs/131.txt index 1e6b69ced..df7c0f2b0 100644 --- a/fastlane/metadata/android/en-US/changelogs/131.txt +++ b/fastlane/metadata/android/en-US/changelogs/131.txt @@ -1,6 +1,6 @@ * OpenStreetMap option to be prompted before logging starts -* Switching to Android WorkManager for sending and logging tasks. Should be more reliable. -* Experimental - broadcast is sent when file upload completed, useful for automation to clean up files. -* Clarified autosend description, that a dynamic file name change also results in the file being sent. -* Error notifications if permissions have been revoked or file writes fail. -* New Custom URL parameter, %SPD_KPH \ No newline at end of file +* Switching to Android WorkManager for sending tasks, should be more reliable. +* Broadcast is sent when file upload completed, useful for automation to clean up files. +* Error notifications and less crashing if permissions have been revoked or file writes fail. +* New Custom URL parameter, %SPD_KPH +* Clarified autosend description: dynamic file name change also results in the file being sent. \ No newline at end of file