diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index a87569e9a..79e391421 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -410,10 +410,8 @@ limitations under the License.
+ android:permission="android.permission.BIND_JOB_SERVICE"/>
diff --git a/src/main/java/de/dennisguse/opentracks/AbstractTrackDeleteActivity.java b/src/main/java/de/dennisguse/opentracks/AbstractTrackDeleteActivity.java
index cf76d7eee..2bb463335 100644
--- a/src/main/java/de/dennisguse/opentracks/AbstractTrackDeleteActivity.java
+++ b/src/main/java/de/dennisguse/opentracks/AbstractTrackDeleteActivity.java
@@ -16,6 +16,7 @@
package de.dennisguse.opentracks;
+import android.os.Handler;
import android.widget.Toast;
import java.util.ArrayList;
@@ -24,7 +25,6 @@ import java.util.stream.Collectors;
import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.services.TrackDeleteService;
-import de.dennisguse.opentracks.services.TrackDeleteServiceConnection;
import de.dennisguse.opentracks.ui.aggregatedStatistics.ConfirmDeleteDialogFragment;
import de.dennisguse.opentracks.ui.aggregatedStatistics.ConfirmDeleteDialogFragment.ConfirmDeleteCaller;
@@ -37,31 +37,8 @@ import de.dennisguse.opentracks.ui.aggregatedStatistics.ConfirmDeleteDialogFragm
* @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, TrackDeleteServiceConnection.Listener {
+public abstract class AbstractTrackDeleteActivity extends AbstractActivity implements ConfirmDeleteCaller, TrackDeleteService.TrackDeleteResultReceiver.Receiver {
- private TrackDeleteServiceConnection trackDeleteServiceConnection;
-
- @Override
- protected void onStart() {
- super.onStart();
- trackDeleteServiceConnection = new TrackDeleteServiceConnection(this);
- trackDeleteServiceConnection.bind(this);
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- if (trackDeleteServiceConnection != null) {
- trackDeleteServiceConnection.unbind(this);
- trackDeleteServiceConnection = null;
- }
- }
-
- /**
- * Delete tracks.
- *
- * @param trackIds the track ids
- */
protected void deleteTracks(Track.Id... trackIds) {
ConfirmDeleteDialogFragment.showDialog(getSupportFragmentManager(), trackIds);
}
@@ -77,29 +54,10 @@ public abstract class AbstractTrackDeleteActivity extends AbstractActivity imple
Toast.makeText(this, getString(R.string.track_delete_not_recording), Toast.LENGTH_LONG).show();
}
- trackDeleteServiceConnection = new TrackDeleteServiceConnection(this);
- trackDeleteServiceConnection.startAndBind(this, trackIdList);
- }
-
- /**
- * Called every time a track is deleted.
- */
- protected void onTrackDeleteStatus(TrackDeleteService.DeletionFinishedStatus deletionFinishedStatus) {
- if (trackDeleteServiceConnection != null) {
- trackDeleteServiceConnection.unbind(this);
- trackDeleteServiceConnection = null;
- onDeleteFinished();
- }
+ TrackDeleteService.enqueue(this, new TrackDeleteService.TrackDeleteResultReceiver(new Handler(), this), trackIdList);
}
protected abstract void onDeleteConfirmed();
- protected abstract void onDeleteFinished();
-
protected abstract Track.Id getRecordingTrackId();
-
- @Override
- public void connected(TrackDeleteService service) {
- service.getDeletingStatusObservable().observe(AbstractTrackDeleteActivity.this, this::onTrackDeleteStatus);
- }
}
diff --git a/src/main/java/de/dennisguse/opentracks/TrackListActivity.java b/src/main/java/de/dennisguse/opentracks/TrackListActivity.java
index 53372bc90..584b6f408 100644
--- a/src/main/java/de/dennisguse/opentracks/TrackListActivity.java
+++ b/src/main/java/de/dennisguse/opentracks/TrackListActivity.java
@@ -422,7 +422,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
}
@Override
- protected void onDeleteFinished() {
+ public void onDeleteFinished() {
// Do nothing
}
diff --git a/src/main/java/de/dennisguse/opentracks/TrackRecordedActivity.java b/src/main/java/de/dennisguse/opentracks/TrackRecordedActivity.java
index cba70be8b..f47e6b7c8 100644
--- a/src/main/java/de/dennisguse/opentracks/TrackRecordedActivity.java
+++ b/src/main/java/de/dennisguse/opentracks/TrackRecordedActivity.java
@@ -40,7 +40,6 @@ import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.databinding.TrackRecordedBinding;
import de.dennisguse.opentracks.fragments.StatisticsRecordedFragment;
import de.dennisguse.opentracks.services.RecordingStatus;
-import de.dennisguse.opentracks.services.TrackDeleteService;
import de.dennisguse.opentracks.services.TrackRecordingService;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.settings.SettingsActivity;
@@ -230,22 +229,13 @@ public class TrackRecordedActivity extends AbstractTrackDeleteActivity implement
return recordingStatus.getTrackId();
}
- @Override
- protected void onTrackDeleteStatus(TrackDeleteService.DeletionFinishedStatus deletionFinishedStatus) {
- super.onTrackDeleteStatus(deletionFinishedStatus);
- if (deletionFinishedStatus.isDeleted(trackId)) {
- runOnUiThread(this::finish);
- }
- }
-
@Override
protected void onDeleteConfirmed() {
runOnUiThread(this::finish);
}
- @Override
- protected void onDeleteFinished() {
- // Do nothing
+ public void onDeleteFinished() {
+ runOnUiThread(this::finish);
}
/**
diff --git a/src/main/java/de/dennisguse/opentracks/TrackStoppedActivity.java b/src/main/java/de/dennisguse/opentracks/TrackStoppedActivity.java
index 14becbb88..03ddd5fc4 100644
--- a/src/main/java/de/dennisguse/opentracks/TrackStoppedActivity.java
+++ b/src/main/java/de/dennisguse/opentracks/TrackStoppedActivity.java
@@ -158,7 +158,7 @@ public class TrackStoppedActivity extends AbstractTrackDeleteActivity implements
}
@Override
- protected void onDeleteFinished() {
+ public void onDeleteFinished() {
finish();
}
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
index b97e6e1bf..51538769b 100644
--- a/src/main/java/de/dennisguse/opentracks/io/file/exporter/ExportService.java
+++ b/src/main/java/de/dennisguse/opentracks/io/file/exporter/ExportService.java
@@ -59,10 +59,6 @@ public class ExportService extends JobIntentService {
resultReceiver.send(resultCode, bundle);
}
- /**
- * Create a new ResultReceive to receive results.
- * Your {@link #onReceiveResult} method will be called from the thread running handler if given, or from an arbitrary thread if null.
- */
public static class ExportServiceResultReceiver extends ResultReceiver {
public static final int RESULT_CODE_SUCCESS = 1;
diff --git a/src/main/java/de/dennisguse/opentracks/services/TrackDeleteService.java b/src/main/java/de/dennisguse/opentracks/services/TrackDeleteService.java
index 590846b21..3136ae731 100644
--- a/src/main/java/de/dennisguse/opentracks/services/TrackDeleteService.java
+++ b/src/main/java/de/dennisguse/opentracks/services/TrackDeleteService.java
@@ -1,146 +1,67 @@
package de.dennisguse.opentracks.services;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.Service;
+import android.app.job.JobService;
import android.content.Context;
import android.content.Intent;
-import android.os.Build;
-import android.os.IBinder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.ResultReceiver;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.core.app.NotificationCompat;
-import androidx.lifecycle.LiveData;
-import androidx.lifecycle.MutableLiveData;
+import androidx.core.app.JobIntentService;
import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.data.ContentProviderUtils;
import de.dennisguse.opentracks.data.models.Track;
-public class TrackDeleteService extends Service {
+public class TrackDeleteService extends JobIntentService {
- private static final String CHANNEL_ID = TrackDeleteService.class.getSimpleName();
- private static final int NOTIFICATION_ID = 1;
+ private static final int JOB_ID = 3;
- static final String EXTRA_TRACK_IDS = "extra_track_ids";
+ private static final String EXTRA_RECEIVER = "extra_receiver";
- private final Binder binder = new Binder();
- private ExecutorService serviceExecutor;
- private MutableLiveData deleteResultObservable;
- private NotificationManager notificationManager;
- private NotificationCompat.Builder notificationBuilder;
+ private static final String EXTRA_TRACK_IDS = "extra_track_ids";
- @Override
- public void onCreate() {
- serviceExecutor = Executors.newSingleThreadExecutor();
- deleteResultObservable = new MutableLiveData<>();
+ public static void enqueue(Context context, TrackDeleteResultReceiver receiver, ArrayList toBeDeleted) {
+ Intent intent = new Intent(context, JobService.class);
+ intent.putExtra(EXTRA_RECEIVER, receiver);
+ intent.putParcelableArrayListExtra(EXTRA_TRACK_IDS, toBeDeleted);
+ enqueueWork(context, TrackDeleteService.class, JOB_ID, intent);
}
@Override
- public void onDestroy() {
- super.onDestroy();
- if (serviceExecutor != null) {
- serviceExecutor.shutdownNow();
- serviceExecutor = null;
- }
- deleteResultObservable = null;
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
+ protected void onHandleWork(@NonNull Intent intent) {
+ ResultReceiver resultReceiver = intent.getParcelableExtra(EXTRA_RECEIVER);
ArrayList trackIds = intent.getParcelableArrayListExtra(EXTRA_TRACK_IDS);
- createAndShowNotification(trackIds.size());
- deleteTracks(trackIds);
- return START_NOT_STICKY;
+
+ ContentProviderUtils contentProviderUtils = new ContentProviderUtils(this);
+ contentProviderUtils.deleteTracks(this, trackIds);
+
+ resultReceiver.send(TrackDeleteResultReceiver.RESULT_CODE_SUCCESS, new Bundle());
}
- private void deleteTracks(@NonNull List trackIds) {
- serviceExecutor.execute(() -> {
- ContentProviderUtils contentProviderUtils = new ContentProviderUtils(this);
- contentProviderUtils.deleteTracks(this, trackIds);
- sendResult(trackIds);
- stopSelf();
- });
- }
+ public static class TrackDeleteResultReceiver extends ResultReceiver {
- private void sendResult(List trackIds) {
- if (deleteResultObservable != null) {
- deleteResultObservable.postValue(new DeletionFinishedStatus(trackIds));
- }
- }
+ public static final int RESULT_CODE_SUCCESS = 1;
- @Override
- public IBinder onBind(Intent intent) {
- return binder;
- }
+ private final Receiver receiver;
- public LiveData getDeletingStatusObservable() {
- return deleteResultObservable;
- }
-
- /**
- * Starts and shows the notification.
- *
- * @param tracksToDelete number of tracks to be deleted.
- */
- private void createAndShowNotification(int tracksToDelete) {
- notificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID, this.getString(R.string.app_name), NotificationManager.IMPORTANCE_LOW);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- notificationChannel.setAllowBubbles(true);
- }
-
- notificationManager.createNotificationChannel(notificationChannel);
+ public TrackDeleteResultReceiver(Handler handler, @NonNull Receiver receiver) {
+ super(handler);
+ this.receiver = receiver;
}
- notificationBuilder = new NotificationCompat.Builder(this, CHANNEL_ID);
- notificationBuilder
- .setContentTitle(this.getString(R.string.track_delete_progress_message))
- .setContentText(this.getString(R.string.track_delete_number_of_tracks, tracksToDelete))
- .setSmallIcon(R.drawable.ic_logo_color_24dp)
- .setProgress(0, 0, true);
-
- startForeground(NOTIFICATION_ID, notificationBuilder.build());
- }
-
- public class Binder extends android.os.Binder {
-
- private Binder() {
- super();
- }
-
- public TrackDeleteService getService() {
- return TrackDeleteService.this;
- }
- }
-
- public static class DeletionFinishedStatus {
- private final List trackIds;
-
- /**
- * @param trackIds List of deleted Track.Ids.
- */
- private DeletionFinishedStatus(@Nullable List trackIds) {
- this.trackIds = trackIds;
- }
-
- public boolean isDeleted(Track.Id trackId) {
- return this.trackIds != null && this.trackIds.contains(trackId);
- }
-
- @NonNull
@Override
- public String toString() {
- return "DeleteStatus{" +
- "trackIds=" + trackIds +
- '}';
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ switch (resultCode) {
+ case RESULT_CODE_SUCCESS -> receiver.onDeleteFinished();
+ default -> throw new RuntimeException("Unknown resultCode.");
+ }
+ }
+
+ public interface Receiver {
+ void onDeleteFinished();
}
}
}
diff --git a/src/main/java/de/dennisguse/opentracks/services/TrackDeleteServiceConnection.java b/src/main/java/de/dennisguse/opentracks/services/TrackDeleteServiceConnection.java
deleted file mode 100644
index fbbb28b85..000000000
--- a/src/main/java/de/dennisguse/opentracks/services/TrackDeleteServiceConnection.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package de.dennisguse.opentracks.services;
-
-import static de.dennisguse.opentracks.services.TrackDeleteService.EXTRA_TRACK_IDS;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.IBinder;
-
-import androidx.annotation.NonNull;
-
-import java.util.ArrayList;
-
-import de.dennisguse.opentracks.BuildConfig;
-import de.dennisguse.opentracks.data.models.Track;
-
-public class TrackDeleteServiceConnection implements ServiceConnection {
-
- final private Listener listener;
- private TrackDeleteService trackDeleteService;
-
- public TrackDeleteServiceConnection(@NonNull Listener listener) {
- this.listener = listener;
- }
-
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- trackDeleteService = ((TrackDeleteService.Binder) service).getService();
- listener.connected(trackDeleteService);
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- trackDeleteService = null;
- }
-
- public void startAndBind(Context context, ArrayList trackIds) {
- if (trackDeleteService != null) {
- return;
- }
-
- Intent intent = new Intent(context, TrackDeleteService.class)
- .putParcelableArrayListExtra(EXTRA_TRACK_IDS, trackIds);
- context.startService(intent);
-
- bind(context);
- }
-
- public void bind(Context context) {
- if (trackDeleteService != null) {
- return;
- }
-
- context.bindService(new Intent(context, TrackDeleteService.class), this, BuildConfig.DEBUG ? Context.BIND_DEBUG_UNBIND : 0);
- }
-
- public void unbind(Context context) {
- context.unbindService(this);
- trackDeleteService = null;
- }
-
- public interface Listener {
- void connected(@NonNull TrackDeleteService service);
- }
-}