From 5bea48096c6ae8819895e4e9211b508455905979 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Wed, 26 Mar 2025 21:48:53 +0100 Subject: [PATCH] TrackList: swipe to delete. --- .../opentracks/AbstractTrackDeleteActivity.java | 1 + .../dennisguse/opentracks/TrackListActivity.java | 16 ++++++++++++++++ .../opentracks/ui/TrackListAdapter.java | 8 ++++++++ .../ConfirmDeleteDialogFragment.java | 6 +++++- .../ui/markers/DeleteMarkerDialogFragment.java | 9 +++++++-- .../opentracks/ui/util/DialogUtils.java | 4 ++-- 6 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/main/java/de/dennisguse/opentracks/AbstractTrackDeleteActivity.java b/src/main/java/de/dennisguse/opentracks/AbstractTrackDeleteActivity.java index 97b8819a6..c7cf063d0 100644 --- a/src/main/java/de/dennisguse/opentracks/AbstractTrackDeleteActivity.java +++ b/src/main/java/de/dennisguse/opentracks/AbstractTrackDeleteActivity.java @@ -43,6 +43,7 @@ public abstract class AbstractTrackDeleteActivity extends AbstractActivity imple ConfirmDeleteDialogFragment.showDialog(getSupportFragmentManager(), trackIds); } + //TODO A callback is better. @Override public void onConfirmDeleteDone(Track.Id... trackIds) { ArrayList trackIdList = Arrays.stream(trackIds) diff --git a/src/main/java/de/dennisguse/opentracks/TrackListActivity.java b/src/main/java/de/dennisguse/opentracks/TrackListActivity.java index ef1227e48..f6ef7a8dd 100644 --- a/src/main/java/de/dennisguse/opentracks/TrackListActivity.java +++ b/src/main/java/de/dennisguse/opentracks/TrackListActivity.java @@ -34,7 +34,9 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.content.res.AppCompatResources; import androidx.core.content.ContextCompat; +import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.button.MaterialButton; @@ -60,6 +62,7 @@ import de.dennisguse.opentracks.ui.aggregatedStatistics.AggregatedStatisticsActi import de.dennisguse.opentracks.ui.aggregatedStatistics.ConfirmDeleteDialogFragment; import de.dennisguse.opentracks.ui.markers.MarkerListActivity; import de.dennisguse.opentracks.ui.util.ActivityUtils; +import de.dennisguse.opentracks.ui.util.RecyclerViewSwipeDeleteCallback; import de.dennisguse.opentracks.util.IntentDashboardUtils; import de.dennisguse.opentracks.util.IntentUtils; import de.dennisguse.opentracks.util.PermissionRequester; @@ -168,6 +171,13 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co viewBinding.trackList.setLayoutManager(layoutManager); viewBinding.trackList.setAdapter(adapter); + ItemTouchHelper itemTouchhelper = new ItemTouchHelper(new RecyclerViewSwipeDeleteCallback(this) { + @Override + public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) { + TrackListActivity.this.deleteTracks(new Track.Id(viewHolder.getItemId())); + } + }); + itemTouchhelper.attachToRecyclerView(viewBinding.trackList); viewBinding.trackListFabAction.setOnClickListener((view) -> { if (recordingStatus.isRecording()) { Toast.makeText(TrackListActivity.this, getString(R.string.hold_to_stop), Toast.LENGTH_LONG).show(); @@ -306,6 +316,12 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co adapter.swapData(tracks); } + @Override + public void onConfirmDeleteAbort(Track.Id... trackIds) { + // This is not nice, but it will happen rarely (as deletion is not a standard case). + adapter.notifyItemRangeChanged(0, adapter.getItemCount()); + } + @Override protected void onDeleteConfirmed() { // Do nothing diff --git a/src/main/java/de/dennisguse/opentracks/ui/TrackListAdapter.java b/src/main/java/de/dennisguse/opentracks/ui/TrackListAdapter.java index fde4f1f24..7a93ec54f 100644 --- a/src/main/java/de/dennisguse/opentracks/ui/TrackListAdapter.java +++ b/src/main/java/de/dennisguse/opentracks/ui/TrackListAdapter.java @@ -55,6 +55,7 @@ public class TrackListAdapter extends RecyclerView.Adapter caller.onConfirmDeleteDone(trackIds) + (dialog, which) -> caller.onConfirmDeleteDone(trackIds), + (dialog, which) -> caller.onConfirmDeleteAbort(trackIds) ); } @@ -86,6 +87,9 @@ public class ConfirmDeleteDialogFragment extends DialogFragment { */ public interface ConfirmDeleteCaller { + default void onConfirmDeleteAbort(Track.Id... trackIds) { + } + void onConfirmDeleteDone(Track.Id... trackIds); } } \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/ui/markers/DeleteMarkerDialogFragment.java b/src/main/java/de/dennisguse/opentracks/ui/markers/DeleteMarkerDialogFragment.java index 6ef11d122..8a7351afa 100644 --- a/src/main/java/de/dennisguse/opentracks/ui/markers/DeleteMarkerDialogFragment.java +++ b/src/main/java/de/dennisguse/opentracks/ui/markers/DeleteMarkerDialogFragment.java @@ -71,13 +71,18 @@ public class DeleteMarkerDialogFragment extends DialogFragment { int titleId = markerIds.length > 1 ? R.string.generic_delete_selected_confirm_title : R.string.marker_delete_one_confirm_title; int messageId = markerIds.length > 1 ? R.string.marker_delete_multiple_confirm_message : R.string.marker_delete_one_confirm_message; return DialogUtils.createConfirmationDialog( - fragmentActivity, titleId, getString(messageId), (dialog, which) -> new Thread(() -> { + fragmentActivity, titleId, getString(messageId), + (dialog, which) -> new Thread(() -> { ContentProviderUtils contentProviderUtils = new ContentProviderUtils(fragmentActivity); for (Marker.Id markerId : markerIds) { contentProviderUtils.deleteMarker(context, markerId); } caller.onMarkerDeleted(); - }).start()); + } + ).start(), + (dialog, which) -> { + // None + }); } /** diff --git a/src/main/java/de/dennisguse/opentracks/ui/util/DialogUtils.java b/src/main/java/de/dennisguse/opentracks/ui/util/DialogUtils.java index 00aba615f..f3b8d2175 100644 --- a/src/main/java/de/dennisguse/opentracks/ui/util/DialogUtils.java +++ b/src/main/java/de/dennisguse/opentracks/ui/util/DialogUtils.java @@ -42,12 +42,12 @@ public class DialogUtils { * @param message the message * @param okListener the listener when OK is clicked */ - public static Dialog createConfirmationDialog(final Context context, int titleId, String message, DialogInterface.OnClickListener okListener) { + public static Dialog createConfirmationDialog(final Context context, int titleId, String message, DialogInterface.OnClickListener okListener, DialogInterface.OnClickListener cancelListener) { return new AlertDialog.Builder(context) .setCancelable(true) .setIcon(R.drawable.ic_delete_forever_24dp) .setMessage(message) - .setNegativeButton(android.R.string.cancel, null) + .setNegativeButton(android.R.string.cancel, cancelListener) .setPositiveButton(android.R.string.ok, okListener) .setTitle(titleId).create(); }