diff --git a/build.gradle b/build.gradle
index d2d1c0c6e7..c9ab30f24b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -152,6 +152,7 @@ dependencies {
implementation 'androidx.core:core-splashscreen:1.0.1'
implementation 'androidx.mediarouter:mediarouter:1.8.1'
implementation 'androidx.core:core-location-altitude:1.0.0-alpha03'
+ implementation 'androidx.work:work-runtime:2.10.2'
androidTestImplementation 'androidx.test:core:1.7.0'
androidTestImplementation 'androidx.test.ext:junit:1.3.0'
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index f0939f3d26..6c8d740ffd 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -395,21 +395,6 @@ limitations under the License.
android:icon="@drawable/ic_logo_color_24dp"
android:label="@string/recording_service" />
-
-
-
-
-
-
{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -31,14 +29,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
}
@NonNull
- protected View createRootView() {
- viewBinding = AboutBinding.inflate(getLayoutInflater());
- return viewBinding.getRoot();
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- viewBinding = null;
+ protected AboutBinding createRootView() {
+ return AboutBinding.inflate(getLayoutInflater());
}
}
diff --git a/src/main/java/de/dennisguse/opentracks/AbstractActivity.java b/src/main/java/de/dennisguse/opentracks/AbstractActivity.java
index 478ef5bf24..f4f1d75745 100644
--- a/src/main/java/de/dennisguse/opentracks/AbstractActivity.java
+++ b/src/main/java/de/dennisguse/opentracks/AbstractActivity.java
@@ -26,14 +26,15 @@
import androidx.core.view.ViewCompat;
import androidx.core.view.ViewGroupCompat;
import androidx.core.view.WindowInsetsCompat;
+import androidx.viewbinding.ViewBinding;
import de.dennisguse.opentracks.services.announcement.TTSManager;
import de.dennisguse.opentracks.settings.PreferencesUtils;
-/**
- * @author Jimmy Shih
- */
-public abstract class AbstractActivity extends AppCompatActivity {
+// TODO Make ViewBinding a generic attribute and set to null in onDestroy()
+public abstract class AbstractActivity extends AppCompatActivity {
+
+ protected T viewBinding;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -46,13 +47,14 @@ protected void onCreate(Bundle savedInstanceState) {
// Set volume control stream for text to speech
setVolumeControlStream(TTSManager.AUDIO_STREAM);
- View rootView = createRootView();
- setContentView(rootView);
+ viewBinding = createRootView();
+ setContentView(viewBinding.getRoot());
- edge2edgeApplyInsets(rootView);
+ edge2edgeApplyInsets();
}
- private void edge2edgeApplyInsets(View rootView) {
+ private void edge2edgeApplyInsets() {
+ View rootView = viewBinding.getRoot();
ViewCompat.setOnApplyWindowInsetsListener(rootView, (view, windowInsets) -> {
Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout() | WindowInsetsCompat.Type.ime());
view.setPadding(insets.left, insets.top, insets.right, insets.bottom);
@@ -69,5 +71,11 @@ public boolean onSupportNavigateUp() {
}
@NonNull
- protected abstract View createRootView();
+ protected abstract T createRootView();
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ viewBinding = null;
+ }
}
diff --git a/src/main/java/de/dennisguse/opentracks/AbstractTrackDeleteActivity.java b/src/main/java/de/dennisguse/opentracks/AbstractTrackDeleteActivity.java
index c7cf063d0f..140e2c4c7d 100644
--- a/src/main/java/de/dennisguse/opentracks/AbstractTrackDeleteActivity.java
+++ b/src/main/java/de/dennisguse/opentracks/AbstractTrackDeleteActivity.java
@@ -16,15 +16,21 @@
package de.dennisguse.opentracks;
-import android.os.Handler;
+import android.view.View;
import android.widget.Toast;
+import androidx.viewbinding.ViewBinding;
+import androidx.work.Data;
+import androidx.work.OneTimeWorkRequest;
+import androidx.work.WorkManager;
+import androidx.work.WorkRequest;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.stream.Collectors;
import de.dennisguse.opentracks.data.models.Track;
-import de.dennisguse.opentracks.services.TrackDeleteService;
+import de.dennisguse.opentracks.services.TrackDeletionWorker;
import de.dennisguse.opentracks.ui.aggregatedStatistics.ConfirmDeleteDialogFragment;
import de.dennisguse.opentracks.ui.aggregatedStatistics.ConfirmDeleteDialogFragment.ConfirmDeleteCaller;
@@ -37,7 +43,7 @@
* @author Jimmy Shih
*/
//TODO Check if this class is still such a good idea; inheritance might limit maintainability
-public abstract class AbstractTrackDeleteActivity extends AbstractActivity implements ConfirmDeleteCaller, TrackDeleteService.TrackDeleteResultReceiver.Receiver {
+public abstract class AbstractTrackDeleteActivity extends AbstractActivity implements ConfirmDeleteCaller {
protected void deleteTracks(Track.Id... trackIds) {
ConfirmDeleteDialogFragment.showDialog(getSupportFragmentManager(), trackIds);
@@ -55,10 +61,29 @@ public void onConfirmDeleteDone(Track.Id... trackIds) {
Toast.makeText(this, getString(R.string.track_delete_not_recording), Toast.LENGTH_LONG).show();
}
- TrackDeleteService.enqueue(this, new TrackDeleteService.TrackDeleteResultReceiver(new Handler(), this), trackIdList);
+ WorkManager workManager = WorkManager.getInstance(this);
+ WorkRequest deleteRequest = new OneTimeWorkRequest.Builder(TrackDeletionWorker.class)
+ .setInputData(new Data.Builder()
+ .putLongArray(TrackDeletionWorker.TRACKIDS_KEY, Arrays.stream(trackIds).mapToLong(Track.Id::id).toArray())
+ .build())
+ .build();
+
+ workManager
+ .getWorkInfoByIdLiveData(deleteRequest.getId())
+ .observe(this, workInfo -> {
+ if (workInfo != null) {
+ if (workInfo.getState().isFinished()) {
+ onDeleteFinished();
+ }
+ }
+ });
+
+ workManager.enqueue(deleteRequest);
}
protected abstract void onDeleteConfirmed();
+ protected abstract void onDeleteFinished();
+
protected abstract Track.Id getRecordingTrackId();
}
diff --git a/src/main/java/de/dennisguse/opentracks/HelpActivity.java b/src/main/java/de/dennisguse/opentracks/HelpActivity.java
index 3c9963df56..cebdc53c46 100644
--- a/src/main/java/de/dennisguse/opentracks/HelpActivity.java
+++ b/src/main/java/de/dennisguse/opentracks/HelpActivity.java
@@ -9,21 +9,18 @@
import de.dennisguse.opentracks.databinding.HelpBinding;
import de.dennisguse.opentracks.ui.util.ViewUtils;
-public class HelpActivity extends AbstractActivity {
-
- private HelpBinding helpBinding;
+public class HelpActivity extends AbstractActivity {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setSupportActionBar(helpBinding.bottomAppBarLayout.bottomAppBar);
+ setSupportActionBar(viewBinding.bottomAppBarLayout.bottomAppBar);
ViewUtils.makeClickableLinks(findViewById(android.R.id.content));
}
@NonNull
@Override
- protected View createRootView() {
- helpBinding = HelpBinding.inflate(getLayoutInflater());
- return helpBinding.getRoot();
+ protected HelpBinding createRootView() {
+ return HelpBinding.inflate(getLayoutInflater());
}
}
diff --git a/src/main/java/de/dennisguse/opentracks/ShowErrorActivity.java b/src/main/java/de/dennisguse/opentracks/ShowErrorActivity.java
index f2f06403f2..91aefe13e9 100644
--- a/src/main/java/de/dennisguse/opentracks/ShowErrorActivity.java
+++ b/src/main/java/de/dennisguse/opentracks/ShowErrorActivity.java
@@ -15,12 +15,10 @@
import de.dennisguse.opentracks.databinding.ActivityShowErrorBinding;
-public class ShowErrorActivity extends AbstractActivity {
+public class ShowErrorActivity extends AbstractActivity {
public static final String EXTRA_ERROR_TEXT = "error";
- private ActivityShowErrorBinding viewBinding;
-
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -32,9 +30,8 @@ public void onCreate(final Bundle savedInstanceState) {
@NonNull
@Override
- protected View createRootView() {
- viewBinding = ActivityShowErrorBinding.inflate(getLayoutInflater());
- return viewBinding.getRoot();
+ protected ActivityShowErrorBinding createRootView() {
+ return ActivityShowErrorBinding.inflate(getLayoutInflater());
}
private String createErrorTitle() {
diff --git a/src/main/java/de/dennisguse/opentracks/TrackEditActivity.java b/src/main/java/de/dennisguse/opentracks/TrackEditActivity.java
index b2728eaeee..47405dd682 100644
--- a/src/main/java/de/dennisguse/opentracks/TrackEditActivity.java
+++ b/src/main/java/de/dennisguse/opentracks/TrackEditActivity.java
@@ -37,7 +37,7 @@
*
* @author Leif Hendrik Wilden
*/
-public class TrackEditActivity extends AbstractActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller {
+public class TrackEditActivity extends AbstractActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller {
public static final String EXTRA_TRACK_ID = "track_id";
@@ -49,8 +49,6 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
private Track track;
private ActivityType activityType;
- private TrackEditBinding viewBinding;
-
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
@@ -125,9 +123,8 @@ public void onSaveInstanceState(@NonNull Bundle outState) {
@NonNull
@Override
- protected View createRootView() {
- viewBinding = TrackEditBinding.inflate(getLayoutInflater());
- return viewBinding.getRoot();
+ protected TrackEditBinding createRootView() {
+ return TrackEditBinding.inflate(getLayoutInflater());
}
private void setActivityTypeIcon(ActivityType activityType) {
diff --git a/src/main/java/de/dennisguse/opentracks/TrackListActivity.java b/src/main/java/de/dennisguse/opentracks/TrackListActivity.java
index f6ef7a8dd7..e3fa1f8fbb 100644
--- a/src/main/java/de/dennisguse/opentracks/TrackListActivity.java
+++ b/src/main/java/de/dennisguse/opentracks/TrackListActivity.java
@@ -72,7 +72,7 @@
*
* @author Leif Hendrik Wilden
*/
-public class TrackListActivity extends AbstractTrackDeleteActivity implements ConfirmDeleteDialogFragment.ConfirmDeleteCaller {
+public class TrackListActivity extends AbstractTrackDeleteActivity implements ConfirmDeleteDialogFragment.ConfirmDeleteCaller {
private static final String TAG = TrackListActivity.class.getSimpleName();
@@ -80,8 +80,6 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
private TrackRecordingServiceConnection recordingStatusConnection;
private TrackListAdapter adapter;
- private TrackListBinding viewBinding;
-
// Preferences
private UnitSystem unitSystem = UnitSystem.defaultUnitSystem();
@@ -166,6 +164,13 @@ protected void onCreate(Bundle savedInstanceState) {
}
});
+ viewBinding.trackListSearchView.getEditText().setOnEditorActionListener((v, actionId, event) -> {
+ searchQuery = viewBinding.trackListSearchView.getEditText().getText().toString();
+ viewBinding.trackListSearchView.hide();
+ loadData();
+ return true;
+ });
+
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
adapter = new TrackListAdapter(this, viewBinding.trackList, recordingStatus, unitSystem);
viewBinding.trackList.setLayoutManager(layoutManager);
@@ -248,24 +253,14 @@ protected void onStop() {
@Override
protected void onDestroy() {
super.onDestroy();
- viewBinding = null;
recordingStatusConnection = null;
adapter = null;
}
@NonNull
@Override
- protected View createRootView() {
- viewBinding = TrackListBinding.inflate(getLayoutInflater());
-
- viewBinding.trackListSearchView.getEditText().setOnEditorActionListener((v, actionId, event) -> {
- searchQuery = viewBinding.trackListSearchView.getEditText().getText().toString();
- viewBinding.trackListSearchView.hide();
- loadData();
- return true;
- });
-
- return viewBinding.getRoot();
+ protected TrackListBinding createRootView() {
+ return TrackListBinding.inflate(getLayoutInflater());
}
@Override
diff --git a/src/main/java/de/dennisguse/opentracks/TrackRecordedActivity.java b/src/main/java/de/dennisguse/opentracks/TrackRecordedActivity.java
index 0b2561fb5a..ad281c1780 100644
--- a/src/main/java/de/dennisguse/opentracks/TrackRecordedActivity.java
+++ b/src/main/java/de/dennisguse/opentracks/TrackRecordedActivity.java
@@ -56,7 +56,7 @@
* @author Rodrigo Damazio
*/
//TODO Should not use TrackRecordingServiceConnection; only used to determine if there is NO current recording, to enable resume functionality.
-public class TrackRecordedActivity extends AbstractTrackDeleteActivity implements ConfirmDeleteDialogFragment.ConfirmDeleteCaller, TrackDataHubInterface {
+public class TrackRecordedActivity extends AbstractTrackDeleteActivity implements ConfirmDeleteDialogFragment.ConfirmDeleteCaller, TrackDataHubInterface {
private static final String TAG = TrackRecordedActivity.class.getSimpleName();
@@ -68,8 +68,6 @@ public class TrackRecordedActivity extends AbstractTrackDeleteActivity implement
private TrackDataHub trackDataHub;
- private TrackRecordedBinding viewBinding;
-
private Track.Id trackId;
private RecordingStatus recordingStatus = TrackRecordingService.STATUS_DEFAULT;
@@ -138,9 +136,8 @@ protected void onSaveInstanceState(@NonNull Bundle outState) {
@NonNull
@Override
- protected View createRootView() {
- viewBinding = TrackRecordedBinding.inflate(getLayoutInflater());
- return viewBinding.getRoot();
+ protected TrackRecordedBinding createRootView() {
+ return TrackRecordedBinding.inflate(getLayoutInflater());
}
@Override
diff --git a/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java b/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java
index a257098db5..6354f52a2e 100644
--- a/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java
+++ b/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java
@@ -56,7 +56,7 @@
* @author Leif Hendrik Wilden
* @author Rodrigo Damazio
*/
-public class TrackRecordingActivity extends AbstractActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller, TrackDataHubInterface {
+public class TrackRecordingActivity extends AbstractActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller, TrackDataHubInterface {
public static final String EXTRA_TRACK_ID = "track_id";
@@ -66,13 +66,11 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
private Snackbar snackbar;
- // The following are setFrequency in onCreate
+ // The following are set in onCreate
private ContentProviderUtils contentProviderUtils;
private TrackRecordingServiceConnection trackRecordingServiceConnection;
private TrackDataHub trackDataHub;
- private TrackRecordingBinding viewBinding;
-
private Track.Id trackId;
private RecordingStatus recordingStatus = TrackRecordingService.STATUS_DEFAULT;
@@ -245,15 +243,13 @@ protected void onStop() {
@Override
protected void onDestroy() {
super.onDestroy();
- viewBinding = null;
trackRecordingServiceConnection = null;
}
@NonNull
@Override
- protected View createRootView() {
- viewBinding = TrackRecordingBinding.inflate(getLayoutInflater());
- return viewBinding.getRoot();
+ protected TrackRecordingBinding createRootView() {
+ return TrackRecordingBinding.inflate(getLayoutInflater());
}
@Override
diff --git a/src/main/java/de/dennisguse/opentracks/TrackStoppedActivity.java b/src/main/java/de/dennisguse/opentracks/TrackStoppedActivity.java
index fa4cd50cf3..49eff7d691 100644
--- a/src/main/java/de/dennisguse/opentracks/TrackStoppedActivity.java
+++ b/src/main/java/de/dennisguse/opentracks/TrackStoppedActivity.java
@@ -16,21 +16,19 @@
import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.databinding.TrackStoppedBinding;
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
+import de.dennisguse.opentracks.io.file.exporter.ExportUtils;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.settings.PreferencesUtils;
import de.dennisguse.opentracks.ui.aggregatedStatistics.ConfirmDeleteDialogFragment;
-import de.dennisguse.opentracks.util.ExportUtils;
import de.dennisguse.opentracks.util.IntentUtils;
import de.dennisguse.opentracks.util.StringUtils;
-public class TrackStoppedActivity extends AbstractTrackDeleteActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller {
+public class TrackStoppedActivity extends AbstractTrackDeleteActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller {
private static final String TAG = TrackStoppedActivity.class.getSimpleName();
public static final String EXTRA_TRACK_ID = "track_id";
- private TrackStoppedBinding viewBinding;
-
private Track.Id trackId;
@Override
@@ -113,9 +111,8 @@ private void storeTrackMetaData(ContentProviderUtils contentProviderUtils, Track
@NonNull
@Override
- protected View createRootView() {
- viewBinding = TrackStoppedBinding.inflate(getLayoutInflater());
- return viewBinding.getRoot();
+ protected TrackStoppedBinding createRootView() {
+ return TrackStoppedBinding.inflate(getLayoutInflater());
}
private void setActivityTypeIcon(ActivityType activityType) {
diff --git a/src/main/java/de/dennisguse/opentracks/introduction/IntroductionActivity.java b/src/main/java/de/dennisguse/opentracks/introduction/IntroductionActivity.java
index 36ba0d4625..9a52f3af39 100644
--- a/src/main/java/de/dennisguse/opentracks/introduction/IntroductionActivity.java
+++ b/src/main/java/de/dennisguse/opentracks/introduction/IntroductionActivity.java
@@ -18,9 +18,7 @@
import de.dennisguse.opentracks.settings.PreferencesUtils;
import de.dennisguse.opentracks.util.IntentUtils;
-public class IntroductionActivity extends AbstractActivity {
-
- private IntroductionBinding viewBinding;
+public class IntroductionActivity extends AbstractActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -49,15 +47,8 @@ protected void onCreate(Bundle savedInstanceState) {
@NonNull
@Override
- protected View createRootView() {
- viewBinding = IntroductionBinding.inflate(getLayoutInflater());
- return viewBinding.getRoot();
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- viewBinding = null;
+ protected IntroductionBinding createRootView() {
+ return IntroductionBinding.inflate(getLayoutInflater());
}
private static class CustomFragmentPagerAdapter extends FragmentStateAdapter {
diff --git a/src/main/java/de/dennisguse/opentracks/io/file/exporter/ExportActivity.java b/src/main/java/de/dennisguse/opentracks/io/file/exporter/ExportActivity.java
index f58efd95be..c4ef3f6559 100644
--- a/src/main/java/de/dennisguse/opentracks/io/file/exporter/ExportActivity.java
+++ b/src/main/java/de/dennisguse/opentracks/io/file/exporter/ExportActivity.java
@@ -24,15 +24,20 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import androidx.documentfile.provider.DocumentFile;
+import androidx.work.Data;
+import androidx.work.OneTimeWorkRequest;
+import androidx.work.WorkInfo;
+import androidx.work.WorkManager;
+import androidx.work.WorkRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.stream.Collectors;
+import de.dennisguse.opentracks.AbstractActivity;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.data.ContentProviderUtils;
import de.dennisguse.opentracks.data.models.Track;
@@ -41,7 +46,6 @@
import de.dennisguse.opentracks.io.file.TrackFileFormat;
import de.dennisguse.opentracks.io.file.TrackFilenameGenerator;
import de.dennisguse.opentracks.settings.PreferencesUtils;
-import de.dennisguse.opentracks.util.ExportUtils;
import de.dennisguse.opentracks.util.FileUtils;
/**
@@ -56,7 +60,7 @@
* So, for this check actually a different file name might be used than in the ExportService.
* * Saved state as an object instead of individual values.
*/
-public class ExportActivity extends AppCompatActivity implements ExportService.ExportServiceResultReceiver.Receiver {
+public class ExportActivity extends AbstractActivity {
private static final String TAG = ExportActivity.class.getSimpleName();
@@ -84,8 +88,6 @@ private enum ConflictResolutionStrategy {
private TrackFileFormat trackFileFormat;
private Uri directoryUri;
- private ExportService.ExportServiceResultReceiver resultReceiver;
-
private List directoryFiles;
private int trackExportSuccessCount;
@@ -94,8 +96,6 @@ private enum ConflictResolutionStrategy {
private int trackExportSkippedCount;
private int trackExportTotalCount;
- private ExportActivityBinding viewBinding;
-
private ArrayList trackErrors = new ArrayList<>();
private ConflictResolutionStrategy autoConflict;
@@ -147,8 +147,6 @@ public void run() {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- viewBinding = ExportActivityBinding.inflate(getLayoutInflater());
- setContentView(viewBinding.getRoot());
setSupportActionBar(viewBinding.bottomAppBarLayout.bottomAppBar);
@@ -161,8 +159,6 @@ public void onCreate(Bundle savedInstanceState) {
DocumentFile documentFile = DocumentFile.fromTreeUri(this, directoryUri);
String directoryDisplayName = FileUtils.getPath(documentFile);
- resultReceiver = new ExportService.ExportServiceResultReceiver(new Handler(), this);
-
if (savedInstanceState == null) {
autoConflict = ConflictResolutionStrategy.CONFLICT_NONE;
setProgress();
@@ -190,6 +186,12 @@ public void onCreate(Bundle savedInstanceState) {
viewBinding.exportActivityToolbar.setTitle(getString(R.string.export_progress_message, directoryDisplayName));
}
+ @NonNull
+ @Override
+ protected ExportActivityBinding createRootView() {
+ return ExportActivityBinding.inflate(getLayoutInflater());
+ }
+
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
@@ -231,19 +233,45 @@ private void export(ExportTask exportTask, ConflictResolutionStrategy conflictRe
if (fileExists && conflictResolution == ConflictResolutionStrategy.CONFLICT_NONE) {
conflict(exportTask);
- } else if (fileExists && conflictResolution == ConflictResolutionStrategy.CONFLICT_SKIP) {
+ return;
+ }
+ if (fileExists && conflictResolution == ConflictResolutionStrategy.CONFLICT_SKIP) {
trackExportSkippedCount++;
nextExport(exportTask);
- } else {
- ExportService.enqueue(this, resultReceiver, exportTask, directoryUri);
+ return;
}
+
+ WorkManager workManager = WorkManager.getInstance(this);
+ WorkRequest exportRequest = new OneTimeWorkRequest.Builder(ExportWorker.class)
+ .setInputData(new Data.Builder()
+ .putLongArray(ExportWorker.TRACKIDS_KEY, exportTask.getTrackIds().stream().mapToLong(Track.Id::id).toArray())
+ .putString(ExportWorker.DIRECTORY_URI_KEY, directoryUri.toString())
+ .putString(ExportWorker.TRACKFILEFORMAT_KEY, exportTask.getTrackFileFormat().toString())
+ .putString(ExportWorker.FILENAME_KEY, exportTask.getFilename())
+ .build())
+ .build();
+
+ workManager
+ .getWorkInfoByIdLiveData(exportRequest.getId())
+ .observe(this, workInfo -> {
+ if (workInfo != null) {
+ WorkInfo.State state = workInfo.getState();
+ switch (state) {
+ case SUCCEEDED -> onExportSuccess(exportTask);
+ case FAILED ->
+ onExportError(exportTask, workInfo.getOutputData().getString(ExportWorker.RESULT_EXPORT_ERROR_MESSAGE_KEY));
+ }
+ }
+ });
+
+ workManager.enqueue(exportRequest);
}
private void export(ExportTask exportTask) {
export(exportTask, autoConflict);
}
- @Deprecated //TODO Check should be done in ExportService
+ @Deprecated //TODO Check should be done in ExportWorker
private boolean exportFileExists(ExportTask exportTask) {
String filename;
if (exportTask.isMultiExport()) {
@@ -314,7 +342,6 @@ private void onExportEnded() {
}
}
- @Override
public void onExportSuccess(ExportTask exportTask) {
if (exportFileExists(exportTask)) {
trackExportOverwrittenCount++;
@@ -325,7 +352,6 @@ public void onExportSuccess(ExportTask exportTask) {
nextExport(exportTask);
}
- @Override
public void onExportError(ExportTask exportTask, String errorMessage) {
trackExportErrorCount++;
String name;
diff --git a/src/main/java/de/dennisguse/opentracks/io/file/exporter/ExportService.java b/src/main/java/de/dennisguse/opentracks/io/file/exporter/ExportService.java
deleted file mode 100644
index 871a3cb53a..0000000000
--- a/src/main/java/de/dennisguse/opentracks/io/file/exporter/ExportService.java
+++ /dev/null
@@ -1,103 +0,0 @@
-package de.dennisguse.opentracks.io.file.exporter;
-
-import android.app.job.JobService;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.ResultReceiver;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.core.app.JobIntentService;
-import androidx.documentfile.provider.DocumentFile;
-
-import de.dennisguse.opentracks.R;
-import de.dennisguse.opentracks.util.ExportUtils;
-
-public class ExportService extends JobIntentService {
-
- private static final String TAG = ExportService.class.getSimpleName();
-
- private static final int JOB_ID = 1;
-
- private static final String EXTRA_RECEIVER = "extra_receiver";
- private static final String EXTRA_EXPORT_TASK = "export_task";
- private static final String EXTRA_DIRECTORY_URI = "extra_directory_uri";
-
- public static void enqueue(Context context, ExportServiceResultReceiver receiver, ExportTask exportTask, Uri directoryUri) {
- Intent intent = new Intent(context, JobService.class);
- intent.putExtra(EXTRA_RECEIVER, receiver);
- intent.putExtra(EXTRA_EXPORT_TASK, exportTask);
- intent.putExtra(EXTRA_DIRECTORY_URI, directoryUri);
- enqueueWork(context, ExportService.class, JOB_ID, intent);
- }
-
- @Override
- protected void onHandleWork(@NonNull Intent intent) {
- // Get all data.
- ResultReceiver resultReceiver = intent.getParcelableExtra(EXTRA_RECEIVER);
- ExportTask exportTask = intent.getParcelableExtra(EXTRA_EXPORT_TASK);
- Uri directoryUri = intent.getParcelableExtra(EXTRA_DIRECTORY_URI);
-
- // Prepare resultCode and bundle to send to the receiver.
- Bundle bundle = new Bundle();
- bundle.putParcelable(ExportServiceResultReceiver.RESULT_EXTRA_EXPORT_TASK, exportTask);
-
- // Build directory file.
- DocumentFile directoryFile = DocumentFile.fromTreeUri(this, directoryUri);
- if (directoryFile == null || !directoryFile.canWrite()) {
- bundle.putString(ExportServiceResultReceiver.EXTRA_EXPORT_ERROR_MESSAGE, getString(R.string.export_cannot_write_to_dir) + ": " + directoryFile);
- resultReceiver.send(ExportServiceResultReceiver.RESULT_CODE_ERROR, bundle);
- return;
- }
-
- // Export and send result
- try {
- ExportUtils.exportTrack(this, directoryFile, exportTask);
- resultReceiver.send(ExportServiceResultReceiver.RESULT_CODE_SUCCESS, bundle);
- } catch (Exception e) {
- Log.e(TAG, "Export failed: " + e);
- e.printStackTrace(); //TODO Remove
-
- bundle.putString(ExportServiceResultReceiver.EXTRA_EXPORT_ERROR_MESSAGE, e.getMessage());
- resultReceiver.send(ExportServiceResultReceiver.RESULT_CODE_ERROR, bundle);
- }
- }
-
- public static class ExportServiceResultReceiver extends ResultReceiver {
-
- public static final int RESULT_CODE_SUCCESS = 1;
- public static final int RESULT_CODE_ERROR = 0;
-
- public static final String RESULT_EXTRA_EXPORT_TASK = "result_extra_export_task";
-
- public static final String EXTRA_EXPORT_ERROR_MESSAGE = "extra_export_error_message";
-
- private final Receiver receiver;
-
- public ExportServiceResultReceiver(Handler handler, @NonNull Receiver receiver) {
- super(handler);
- this.receiver = receiver;
- }
-
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- ExportTask exportTask = resultData.getParcelable(RESULT_EXTRA_EXPORT_TASK);
- switch (resultCode) {
- case RESULT_CODE_SUCCESS -> receiver.onExportSuccess(exportTask);
- case RESULT_CODE_ERROR -> receiver.onExportError(exportTask, resultData.getString(EXTRA_EXPORT_ERROR_MESSAGE));
- default -> throw new RuntimeException("Unknown resultCode.");
- }
- }
-
- public interface Receiver {
- default void onExportSuccess(ExportTask exportTask) {
- }
-
- default void onExportError(ExportTask exportTask, String errorMessage) {
- }
- }
- }
-}
diff --git a/src/main/java/de/dennisguse/opentracks/util/ExportUtils.java b/src/main/java/de/dennisguse/opentracks/io/file/exporter/ExportUtils.java
similarity index 60%
rename from src/main/java/de/dennisguse/opentracks/util/ExportUtils.java
rename to src/main/java/de/dennisguse/opentracks/io/file/exporter/ExportUtils.java
index 552ee12d68..28174b3513 100644
--- a/src/main/java/de/dennisguse/opentracks/util/ExportUtils.java
+++ b/src/main/java/de/dennisguse/opentracks/io/file/exporter/ExportUtils.java
@@ -1,15 +1,21 @@
-package de.dennisguse.opentracks.util;
+package de.dennisguse.opentracks.io.file.exporter;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
-import android.os.Handler;
import android.provider.DocumentsContract;
import android.util.Log;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
import androidx.documentfile.provider.DocumentFile;
+import androidx.work.Data;
+import androidx.work.OneTimeWorkRequest;
+import androidx.work.WorkInfo;
+import androidx.work.WorkManager;
+import androidx.work.WorkRequest;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -22,36 +28,79 @@
import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
import de.dennisguse.opentracks.io.file.TrackFilenameGenerator;
-import de.dennisguse.opentracks.io.file.exporter.ExportService;
-import de.dennisguse.opentracks.io.file.exporter.ExportService.ExportServiceResultReceiver;
-import de.dennisguse.opentracks.io.file.exporter.ExportTask;
-import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
import de.dennisguse.opentracks.settings.PreferencesUtils;
import de.dennisguse.opentracks.settings.SettingsActivity;
+import de.dennisguse.opentracks.util.IntentUtils;
public class ExportUtils {
private static final String TAG = ExportUtils.class.getSimpleName();
- public static void postWorkoutExport(Context context, Track.Id trackId) {
+ public static void postWorkoutExport(AppCompatActivity context, Track.Id trackId) {
if (PreferencesUtils.shouldInstantExportAfterWorkout()) {
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat();
DocumentFile directory = IntentUtils.toDocumentFile(context, PreferencesUtils.getDefaultExportDirectoryUri());
- ExportServiceResultReceiver resultReceiver = new ExportServiceResultReceiver(new Handler(), new ExportServiceResultReceiver.Receiver() {
- @Override
- public void onExportError(ExportTask unused, String errorMessage) {
- Intent intent = new Intent(context, SettingsActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.putExtra(SettingsActivity.EXTRAS_EXPORT_ERROR_MESSAGE, errorMessage);
- context.startActivity(intent);
- }
- });
+ WorkManager workManager = WorkManager.getInstance(context);
+ WorkRequest exportRequest = new OneTimeWorkRequest.Builder(ExportWorker.class)
+ .setInputData(new Data.Builder()
+ .putLongArray(ExportWorker.TRACKIDS_KEY, new long[]{trackId.id()})
+ .putString(ExportWorker.DIRECTORY_URI_KEY, directory.toString())
+ .putString(ExportWorker.TRACKFILEFORMAT_KEY, trackFileFormat.toString())
+ .putString(ExportWorker.FILENAME_KEY, null)
+ .build())
+ .build();
+
+ workManager
+ .getWorkInfoByIdLiveData(exportRequest.getId())
+ .observe(context, workInfo -> {
+ if (workInfo != null) {
+ if (workInfo.getState() == WorkInfo.State.FAILED) {
+ Intent intent = new Intent(context, SettingsActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(SettingsActivity.EXTRAS_EXPORT_ERROR_MESSAGE, workInfo.getProgress().getString(ExportWorker.RESULT_EXPORT_ERROR_MESSAGE_KEY));
+ context.startActivity(intent);
+ }
+ }
+ });
+
+ workManager.enqueue(exportRequest);
+ }
+ }
+
+ public static void exportTrack(Context context, DocumentFile directory, @Nullable String filenameForMultiple, TrackFileFormat trackFileFormat, List trackIds) {
+
+ ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
+ List