forked from upstream-mirrors/OpenTracks
TrackDeleteService can be a JobIntentService (rather than ForegroundService).
Android 14 requires ForegroundService to have a reason. Fixes #1657.
This commit is contained in:
@@ -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<DeletionFinishedStatus> 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<Track.Id> 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<Track.Id> 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<Track.Id> 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<Track.Id> 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<DeletionFinishedStatus> 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<Track.Id> trackIds;
|
||||
|
||||
/**
|
||||
* @param trackIds List of deleted Track.Ids.
|
||||
*/
|
||||
private DeletionFinishedStatus(@Nullable List<Track.Id> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user