summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatej Drobnič <matej@matejdro.com>2023-10-22 16:53:41 +0200
committerGitHub <noreply@github.com>2023-10-22 16:53:41 +0200
commit346365b8d0aef14e79da921056d9438fe9d2663e (patch)
tree5fa0082c45493ffa16ffd0a63a3db722fd3206a1
parentfa75317bce95bdae152fa1171894d4c025e3921c (diff)
downloadAntennaPod-346365b8d0aef14e79da921056d9438fe9d2663e.zip
Delete local feed episodes (#6400)
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java17
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DeleteActionButton.java14
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java1
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java1
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/actions/EpisodeMultiSelectActionHandler.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/DownloadsPreferencesFragment.java27
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/swipeactions/DeleteSwipeAction.java12
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java14
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/LocalDeleteModal.java32
-rw-r--r--app/src/main/res/xml/preferences_downloads.xml6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java5
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java128
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/FeedUtil.java13
-rw-r--r--storage/preferences/src/main/java/de/danoeh/antennapod/storage/preferences/UserPreferences.java5
-rw-r--r--ui/i18n/src/main/res/values/strings.xml5
17 files changed, 224 insertions, 67 deletions
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java b/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
index 1f387b24b..24c20242a 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
@@ -41,6 +41,7 @@ import static de.test.antennapod.EspressoTestUtils.clickPreference;
import static de.test.antennapod.EspressoTestUtils.waitForView;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@LargeTest
@@ -200,6 +201,22 @@ public class PreferencesTest {
}
@Test
+ public void testAutoDeleteLocal() {
+ clickPreference(R.string.downloads_pref);
+ final boolean initialAutoDelete = UserPreferences.isAutoDeleteLocal();
+ assertFalse(initialAutoDelete);
+
+ onView(withText(R.string.pref_auto_local_delete_title)).perform(click());
+ onView(withText(R.string.yes)).perform(click());
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.isAutoDeleteLocal());
+
+ onView(withText(R.string.pref_auto_local_delete_title)).perform(click());
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> !UserPreferences.isAutoDeleteLocal());
+ }
+
+ @Test
public void testPlaybackSpeeds() {
clickPreference(R.string.playback_pref);
clickPreference(R.string.playback_speed);
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DeleteActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DeleteActionButton.java
index 096d060c1..16a5a161c 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DeleteActionButton.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DeleteActionButton.java
@@ -4,10 +4,14 @@ import android.content.Context;
import android.view.View;
import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
+
+import java.util.Collections;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.view.LocalDeleteModal;
public class DeleteActionButton extends ItemActionButton {
@@ -33,11 +37,17 @@ public class DeleteActionButton extends ItemActionButton {
if (media == null) {
return;
}
- DBWriter.deleteFeedMediaOfItem(context, media.getId());
+
+ LocalDeleteModal.showLocalFeedDeleteWarningIfNecessary(context, Collections.singletonList(item),
+ () -> DBWriter.deleteFeedMediaOfItem(context, media.getId()));
}
@Override
public int getVisibility() {
- return (item.getMedia() != null && item.getMedia().isDownloaded()) ? View.VISIBLE : View.INVISIBLE;
+ if (item.getMedia() != null && (item.getMedia().isDownloaded() || item.getFeed().isLocalFeed())) {
+ return View.VISIBLE;
+ }
+
+ return View.INVISIBLE;
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
index 6a72348bc..29be41727 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
@@ -197,8 +197,8 @@ public class AddFeedFragment extends Fragment {
}
private Feed addLocalFolder(Uri uri) {
- getActivity().getContentResolver()
- .takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ getActivity().getContentResolver().takePersistableUriPermission(uri,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
DocumentFile documentFile = DocumentFile.fromTreeUri(getContext(), uri);
if (documentFile == null) {
throw new IllegalArgumentException("Unable to retrieve document tree");
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
index 6b6fedd1f..1c949218a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
@@ -367,7 +367,6 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
swipeActions.detach();
if (feed.isLocalFeed()) {
speedDialBinding.fabSD.removeActionItemById(R.id.download_batch);
- speedDialBinding.fabSD.removeActionItemById(R.id.delete_batch);
}
speedDialBinding.fabSD.removeActionItemById(R.id.remove_all_inbox_item);
speedDialBinding.fabSD.setVisibility(View.VISIBLE);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java
index 0e4c883cf..1df1e02a2 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java
@@ -172,7 +172,6 @@ public class FeedSettingsFragment extends Fragment {
if (feed.isLocalFeed()) {
findPreference(PREF_AUTHENTICATION).setVisible(false);
- findPreference(PREF_AUTO_DELETE).setVisible(false);
findPreference(PREF_CATEGORY_AUTO_DOWNLOAD).setVisible(false);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/actions/EpisodeMultiSelectActionHandler.java b/app/src/main/java/de/danoeh/antennapod/fragment/actions/EpisodeMultiSelectActionHandler.java
index a14bfcd16..618c411ea 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/actions/EpisodeMultiSelectActionHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/actions/EpisodeMultiSelectActionHandler.java
@@ -14,6 +14,7 @@ import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterfa
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.view.LocalDeleteModal;
public class EpisodeMultiSelectActionHandler {
private static final String TAG = "EpisodeSelectHandler";
@@ -41,7 +42,7 @@ public class EpisodeMultiSelectActionHandler {
} else if (actionId == R.id.download_batch) {
downloadChecked(items);
} else if (actionId == R.id.delete_batch) {
- deleteChecked(items);
+ LocalDeleteModal.showLocalFeedDeleteWarningIfNecessary(activity, items, () -> deleteChecked(items));
} else {
Log.e(TAG, "Unrecognized speed dial action item. Do nothing. id=" + actionId);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/DownloadsPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/DownloadsPreferencesFragment.java
index 7b0c3efdf..c17066fef 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/DownloadsPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/DownloadsPreferencesFragment.java
@@ -2,8 +2,12 @@ package de.danoeh.antennapod.fragment.preferences;
import android.content.SharedPreferences;
import android.os.Bundle;
+
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceManager;
+import androidx.preference.TwoStatePreference;
+
+import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.core.util.download.FeedUpdateManager;
@@ -17,9 +21,12 @@ import java.io.File;
public class DownloadsPreferencesFragment extends PreferenceFragmentCompat
implements SharedPreferences.OnSharedPreferenceChangeListener {
private static final String PREF_SCREEN_AUTODL = "prefAutoDownloadSettings";
+ private static final String PREF_AUTO_DELETE_LOCAL = "prefAutoDeleteLocal";
private static final String PREF_PROXY = "prefProxy";
private static final String PREF_CHOOSE_DATA_DIR = "prefChooseDataDir";
+ private boolean blockAutoDeleteLocal = true;
+
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.preferences_downloads);
@@ -63,6 +70,14 @@ public class DownloadsPreferencesFragment extends PreferenceFragmentCompat
});
return true;
});
+ findPreference(PREF_AUTO_DELETE_LOCAL).setOnPreferenceChangeListener((preference, newValue) -> {
+ if (blockAutoDeleteLocal && newValue == Boolean.TRUE) {
+ showAutoDeleteEnableDialog();
+ return false;
+ } else {
+ return true;
+ }
+ });
}
private void setDataFolderText() {
@@ -78,4 +93,16 @@ public class DownloadsPreferencesFragment extends PreferenceFragmentCompat
FeedUpdateManager.restartUpdateAlarm(getContext(), true);
}
}
+
+ private void showAutoDeleteEnableDialog() {
+ new MaterialAlertDialogBuilder(requireContext())
+ .setMessage(R.string.pref_auto_local_delete_dialog_body)
+ .setPositiveButton(R.string.yes, (dialog, which) -> {
+ blockAutoDeleteLocal = false;
+ ((TwoStatePreference) findPreference(PREF_AUTO_DELETE_LOCAL)).setChecked(true);
+ blockAutoDeleteLocal = true;
+ })
+ .setNegativeButton(R.string.cancel_label, null)
+ .show();
+ }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/swipeactions/DeleteSwipeAction.java b/app/src/main/java/de/danoeh/antennapod/fragment/swipeactions/DeleteSwipeAction.java
index a4c69eb2b..e196b96b4 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/swipeactions/DeleteSwipeAction.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/swipeactions/DeleteSwipeAction.java
@@ -2,10 +2,14 @@ package de.danoeh.antennapod.fragment.swipeactions;
import android.content.Context;
import androidx.fragment.app.Fragment;
+
+import java.util.Collections;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedItemFilter;
+import de.danoeh.antennapod.view.LocalDeleteModal;
public class DeleteSwipeAction implements SwipeAction {
@@ -31,14 +35,16 @@ public class DeleteSwipeAction implements SwipeAction {
@Override
public void performAction(FeedItem item, Fragment fragment, FeedItemFilter filter) {
- if (!item.isDownloaded()) {
+ if (!item.isDownloaded() && !item.getFeed().isLocalFeed()) {
return;
}
- DBWriter.deleteFeedMediaOfItem(fragment.requireContext(), item.getMedia().getId());
+ LocalDeleteModal.showLocalFeedDeleteWarningIfNecessary(
+ fragment.requireContext(), Collections.singletonList(item),
+ () -> DBWriter.deleteFeedMediaOfItem(fragment.requireContext(), item.getMedia().getId()));
}
@Override
public boolean willRemove(FeedItemFilter filter, FeedItem item) {
- return filter.showDownloaded && item.isDownloaded();
+ return filter.showDownloaded && (item.isDownloaded() || item.getFeed().isLocalFeed());
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
index de4cec8fe..a72b32497 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
@@ -12,10 +12,12 @@ import androidx.fragment.app.Fragment;
import com.google.android.material.snackbar.Snackbar;
+import java.util.Arrays;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
-import de.danoeh.antennapod.storage.preferences.UserPreferences;
+import de.danoeh.antennapod.core.util.FeedUtil;
import de.danoeh.antennapod.core.receiver.MediaButtonReceiver;
import de.danoeh.antennapod.core.service.playback.PlaybackServiceInterface;
import de.danoeh.antennapod.core.storage.DBWriter;
@@ -29,6 +31,7 @@ import de.danoeh.antennapod.dialog.ShareDialog;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.net.sync.model.EpisodeAction;
+import de.danoeh.antennapod.view.LocalDeleteModal;
/**
* Handles interactions with the FeedItemMenu.
@@ -56,6 +59,7 @@ public class FeedItemMenuHandler {
final boolean isPlaying = hasMedia && PlaybackStatus.isPlaying(selectedItem.getMedia());
final boolean isInQueue = selectedItem.isTagged(FeedItem.TAG_QUEUE);
final boolean fileDownloaded = hasMedia && selectedItem.getMedia().fileExists();
+ final boolean isLocalFile = hasMedia && selectedItem.getFeed().isLocalFeed();
final boolean isFavorite = selectedItem.isTagged(FeedItem.TAG_FAVORITE);
setItemVisibility(menu, R.id.skip_episode_item, isPlaying);
@@ -80,7 +84,7 @@ public class FeedItemMenuHandler {
setItemVisibility(menu, R.id.add_to_favorites_item, !isFavorite);
setItemVisibility(menu, R.id.remove_from_favorites_item, isFavorite);
- setItemVisibility(menu, R.id.remove_item, fileDownloaded);
+ setItemVisibility(menu, R.id.remove_item, fileDownloaded || isLocalFile);
return true;
}
@@ -148,7 +152,8 @@ public class FeedItemMenuHandler {
if (menuItemId == R.id.skip_episode_item) {
context.sendBroadcast(MediaButtonReceiver.createIntent(context, KeyEvent.KEYCODE_MEDIA_NEXT));
} else if (menuItemId == R.id.remove_item) {
- DBWriter.deleteFeedMediaOfItem(context, selectedItem.getMedia().getId());
+ LocalDeleteModal.showLocalFeedDeleteWarningIfNecessary(context, Arrays.asList(selectedItem),
+ () -> DBWriter.deleteFeedMediaOfItem(context, selectedItem.getMedia().getId()));
} else if (menuItemId == R.id.remove_inbox_item) {
removeNewFlagWithUndo(fragment, selectedItem);
} else if (menuItemId == R.id.mark_read_item) {
@@ -225,7 +230,8 @@ public class FeedItemMenuHandler {
final Handler h = new Handler(fragment.requireContext().getMainLooper());
final Runnable r = () -> {
FeedMedia media = item.getMedia();
- if (media != null && FeedItemUtil.hasAlmostEnded(media) && UserPreferences.isAutoDelete()) {
+ boolean shouldAutoDelete = FeedUtil.shouldAutoDeleteItemsOnThatFeed(item.getFeed());
+ if (media != null && FeedItemUtil.hasAlmostEnded(media) && shouldAutoDelete) {
DBWriter.deleteFeedMediaOfItem(fragment.requireContext(), media.getId());
}
};
diff --git a/app/src/main/java/de/danoeh/antennapod/view/LocalDeleteModal.java b/app/src/main/java/de/danoeh/antennapod/view/LocalDeleteModal.java
new file mode 100644
index 000000000..4241cadca
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/view/LocalDeleteModal.java
@@ -0,0 +1,32 @@
+package de.danoeh.antennapod.view;
+
+import android.content.Context;
+
+import com.google.android.material.dialog.MaterialAlertDialogBuilder;
+import de.danoeh.antennapod.ui.i18n.R;
+import de.danoeh.antennapod.model.feed.FeedItem;
+
+public class LocalDeleteModal {
+ public static void showLocalFeedDeleteWarningIfNecessary(Context context, Iterable<FeedItem> items,
+ Runnable deleteCommand) {
+ boolean anyLocalFeed = false;
+ for (FeedItem item : items) {
+ if (item.getFeed().isLocalFeed()) {
+ anyLocalFeed = true;
+ break;
+ }
+ }
+
+ if (!anyLocalFeed) {
+ deleteCommand.run();
+ return;
+ }
+
+ new MaterialAlertDialogBuilder(context)
+ .setTitle(R.string.delete_episode_label)
+ .setMessage(R.string.delete_local_feed_warning_body)
+ .setPositiveButton(R.string.delete_label, (dialog, which) -> deleteCommand.run())
+ .setNegativeButton(R.string.cancel_label, null)
+ .show();
+ }
+}
diff --git a/app/src/main/res/xml/preferences_downloads.xml b/app/src/main/res/xml/preferences_downloads.xml
index e1a1b9b00..b0a41dfb5 100644
--- a/app/src/main/res/xml/preferences_downloads.xml
+++ b/app/src/main/res/xml/preferences_downloads.xml
@@ -34,6 +34,12 @@
android:summary="@string/pref_auto_delete_sum"
android:title="@string/pref_auto_delete_title"/>
<SwitchPreferenceCompat
+ android:defaultValue="false"
+ android:enabled="true"
+ android:key="prefAutoDeleteLocal"
+ android:summary="@string/pref_auto_local_delete_sum"
+ android:title="@string/pref_auto_local_delete_title"/>
+ <SwitchPreferenceCompat
android:defaultValue="true"
android:enabled="true"
android:key="prefFavoriteKeepsEpisode"
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java b/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java
index 03881ee4f..52b2d61dc 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java
@@ -37,7 +37,6 @@ import de.danoeh.antennapod.model.download.DownloadError;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedMedia;
-import de.danoeh.antennapod.model.feed.FeedPreferences;
import de.danoeh.antennapod.model.playback.MediaType;
import de.danoeh.antennapod.parser.feed.util.MimeTypeUtils;
import de.danoeh.antennapod.parser.media.id3.ID3ReaderException;
@@ -124,13 +123,9 @@ public class LocalFeedUpdater {
feed.setImageUrl(getImageUrl(allFiles, folderUri));
feed.getPreferences().setAutoDownload(false);
- feed.getPreferences().setAutoDeleteAction(FeedPreferences.AutoDeleteAction.NEVER);
feed.setDescription(context.getString(R.string.local_feed_description));
feed.setAuthor(context.getString(R.string.local_folder));
- if (newItems.isEmpty()) {
- throw new IOException("Empty folder. Make sure that the folder is accessible and contains media files.");
- }
DBTasks.updateFeed(context, feed, true);
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
index b98dbfad7..c82d6f975 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
@@ -68,6 +68,7 @@ import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.FeedSearcher;
import de.danoeh.antennapod.core.sync.queue.SynchronizationQueueSink;
import de.danoeh.antennapod.core.util.FeedItemUtil;
+import de.danoeh.antennapod.core.util.FeedUtil;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.gui.NotificationUtils;
@@ -1103,7 +1104,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
FeedPreferences.AutoDeleteAction action =
item.getFeed().getPreferences().getCurrentAutoDelete();
boolean shouldAutoDelete = action == FeedPreferences.AutoDeleteAction.ALWAYS
- || (action == FeedPreferences.AutoDeleteAction.GLOBAL && UserPreferences.isAutoDelete());
+ || (action == FeedPreferences.AutoDeleteAction.GLOBAL
+ && FeedUtil.shouldAutoDeleteItemsOnThatFeed(item.getFeed()));
if (shouldAutoDelete && (!item.isTagged(FeedItem.TAG_FAVORITE)
|| !UserPreferences.shouldFavoriteKeepEpisode())) {
DBWriter.deleteFeedMediaOfItem(PlaybackService.this, media.getId());
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java
index 11e82af27..f7e8592e1 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java
@@ -2,15 +2,22 @@ package de.danoeh.antennapod.core.storage;
import android.app.backup.BackupManager;
import android.content.Context;
+import android.net.Uri;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationManagerCompat;
+import androidx.documentfile.provider.DocumentFile;
+import com.google.common.util.concurrent.Futures;
+
+import de.danoeh.antennapod.core.event.DownloadLogEvent;
+import de.danoeh.antennapod.core.feed.LocalFeedUpdater;
import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
import de.danoeh.antennapod.core.service.playback.PlaybackServiceInterface;
import de.danoeh.antennapod.storage.database.PodDBAdapter;
+
import org.greenrobot.eventbus.EventBus;
import java.io.File;
@@ -25,7 +32,6 @@ import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.R;
-import de.danoeh.antennapod.core.event.DownloadLogEvent;
import de.danoeh.antennapod.event.FavoritesEvent;
import de.danoeh.antennapod.event.FeedItemEvent;
import de.danoeh.antennapod.event.FeedListUpdateEvent;
@@ -94,7 +100,7 @@ public class DBWriter {
*/
public static Future<?> deleteFeedMediaOfItem(@NonNull final Context context,
final long mediaId) {
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
final FeedMedia media = DBReader.getFeedMedia(mediaId);
if (media != null) {
boolean result = deleteFeedMediaSynchronous(context, media);
@@ -106,10 +112,10 @@ public class DBWriter {
});
}
- private static boolean deleteFeedMediaSynchronous(
- @NonNull Context context, @NonNull FeedMedia media) {
+ private static boolean deleteFeedMediaSynchronous(@NonNull Context context, @NonNull FeedMedia media) {
Log.i(TAG, String.format(Locale.US, "Requested to delete FeedMedia [id=%d, title=%s, downloaded=%s",
media.getId(), media.getEpisodeTitle(), media.isDownloaded()));
+ boolean localDelete = false;
if (media.isDownloaded()) {
// delete downloaded media file
File mediaFile = new File(media.getFile_url());
@@ -125,23 +131,38 @@ public class DBWriter {
adapter.open();
adapter.setMedia(media);
adapter.close();
+ } else if (media.getFile_url().startsWith("content://")) {
+ // Local feed
+ DocumentFile documentFile = DocumentFile.fromSingleUri(
+ context, Uri.parse(media.getFile_url()));
+ if (documentFile == null || !documentFile.exists() || !documentFile.delete()) {
+ EventBus.getDefault().post(new MessageEvent(context.getString(R.string.delete_local_failed)));
+ return false;
+ }
+ localDelete = true;
+ }
- if (media.getId() == PlaybackPreferences.getCurrentlyPlayingFeedMediaId()) {
- PlaybackPreferences.writeNoMediaPlaying();
- IntentUtils.sendLocalBroadcast(context, PlaybackServiceInterface.ACTION_SHUTDOWN_PLAYBACK_SERVICE);
+ if (media.getId() == PlaybackPreferences.getCurrentlyPlayingFeedMediaId()) {
+ PlaybackPreferences.writeNoMediaPlaying();
+ IntentUtils.sendLocalBroadcast(context, PlaybackServiceInterface.ACTION_SHUTDOWN_PLAYBACK_SERVICE);
- NotificationManagerCompat nm = NotificationManagerCompat.from(context);
- nm.cancel(R.id.notification_playing);
- }
+ NotificationManagerCompat nm = NotificationManagerCompat.from(context);
+ nm.cancel(R.id.notification_playing);
+ }
+ if (localDelete) {
+ // Do full update of this feed to get rid of the item
+ LocalFeedUpdater.updateFeed(media.getItem().getFeed(), context.getApplicationContext(), null);
+ } else {
// Gpodder: queue delete action for synchronization
FeedItem item = media.getItem();
EpisodeAction action = new EpisodeAction.Builder(item, EpisodeAction.DELETE)
.currentTimestamp()
.build();
SynchronizationQueueSink.enqueueEpisodeActionIfSynchronizationIsActive(context, action);
+
+ EventBus.getDefault().post(FeedItemEvent.updated(media.getItem()));
}
- EventBus.getDefault().post(FeedItemEvent.updated(media.getItem()));
return true;
}
@@ -152,7 +173,7 @@ public class DBWriter {
* @param feedId ID of the Feed that should be deleted.
*/
public static Future<?> deleteFeed(final Context context, final long feedId) {
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
final Feed feed = DBReader.getFeed(feedId);
if (feed == null) {
return;
@@ -183,7 +204,7 @@ public class DBWriter {
*/
@NonNull
public static Future<?> deleteFeedItems(@NonNull Context context, @NonNull List<FeedItem> items) {
- return dbExec.submit(() -> deleteFeedItemsSynchronous(context, items));
+ return runOnDbThread(() -> deleteFeedItemsSynchronous(context, items));
}
/**
@@ -235,7 +256,7 @@ public class DBWriter {
* Deletes the entire playback history.
*/
public static Future<?> clearPlaybackHistory() {
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.clearPlaybackHistory();
@@ -248,7 +269,7 @@ public class DBWriter {
* Deletes the entire download log.
*/
public static Future<?> clearDownloadLog() {
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.clearDownloadLog();
@@ -281,7 +302,7 @@ public class DBWriter {
* @param date PlaybackCompletionDate for <code>media</code>
*/
public static Future<?> addItemToPlaybackHistory(final FeedMedia media, Date date) {
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
Log.d(TAG, "Adding item to playback history");
media.setPlaybackCompletionDate(date);
@@ -300,7 +321,7 @@ public class DBWriter {
* @param status The DownloadStatus object.
*/
public static Future<?> addDownloadStatus(final DownloadResult status) {
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setDownloadStatus(status);
@@ -322,7 +343,7 @@ public class DBWriter {
*/
public static Future<?> addQueueItemAt(final Context context, final long itemId,
final int index, final boolean performAutoDownload) {
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
final PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
final List<FeedItem> queue = DBReader.getQueue(adapter);
@@ -393,7 +414,7 @@ public class DBWriter {
*/
public static Future<?> addQueueItem(final Context context, final boolean performAutoDownload,
final boolean markAsUnplayed, final long... itemIds) {
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
if (itemIds.length < 1) {
return;
}
@@ -476,7 +497,7 @@ public class DBWriter {
* Removes all FeedItem objects from the queue.
*/
public static Future<?> clearQueue() {
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.clearQueue();
@@ -495,12 +516,12 @@ public class DBWriter {
*/
public static Future<?> removeQueueItem(final Context context,
final boolean performAutoDownload, final FeedItem item) {
- return dbExec.submit(() -> removeQueueItemSynchronous(context, performAutoDownload, item.getId()));
+ return runOnDbThread(() -> removeQueueItemSynchronous(context, performAutoDownload, item.getId()));
}
public static Future<?> removeQueueItem(final Context context, final boolean performAutoDownload,
final long... itemIds) {
- return dbExec.submit(() -> removeQueueItemSynchronous(context, performAutoDownload, itemIds));
+ return runOnDbThread(() -> removeQueueItemSynchronous(context, performAutoDownload, itemIds));
}
private static void removeQueueItemSynchronous(final Context context,
@@ -562,7 +583,7 @@ public class DBWriter {
}
public static Future<?> addFavoriteItem(final FeedItem item) {
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
final PodDBAdapter adapter = PodDBAdapter.getInstance().open();
adapter.addFavoriteItem(item);
adapter.close();
@@ -573,7 +594,7 @@ public class DBWriter {
}
public static Future<?> removeFavoriteItem(final FeedItem item) {
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
final PodDBAdapter adapter = PodDBAdapter.getInstance().open();
adapter.removeFavoriteItem(item);
adapter.close();
@@ -590,7 +611,7 @@ public class DBWriter {
* @param broadcastUpdate true if this operation should trigger a QueueUpdateBroadcast. This option should be set to
*/
public static Future<?> moveQueueItemToTop(final long itemId, final boolean broadcastUpdate) {
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
LongList queueIdList = DBReader.getQueueIDList();
int index = queueIdList.indexOf(itemId);
if (index >= 0) {
@@ -609,7 +630,7 @@ public class DBWriter {
*/
public static Future<?> moveQueueItemToBottom(final long itemId,
final boolean broadcastUpdate) {
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
LongList queueIdList = DBReader.getQueueIDList();
int index = queueIdList.indexOf(itemId);
if (index >= 0) {
@@ -632,7 +653,7 @@ public class DBWriter {
*/
public static Future<?> moveQueueItem(final int from,
final int to, final boolean broadcastUpdate) {
- return dbExec.submit(() -> moveQueueItemHelper(from, to, broadcastUpdate));
+ return runOnDbThread(() -> moveQueueItemHelper(from, to, broadcastUpdate));
}
/**
@@ -669,7 +690,7 @@ public class DBWriter {
}
public static Future<?> resetPagedFeedPage(Feed feed) {
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
final PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.resetPagedFeedPage(feed);
@@ -699,7 +720,7 @@ public class DBWriter {
*/
public static Future<?> markItemPlayed(final int played, final boolean broadcastUpdate,
final long... itemIds) {
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
final PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setFeedItemRead(played, itemIds);
@@ -729,7 +750,7 @@ public class DBWriter {
final int played,
final long mediaId,
final boolean resetMediaPosition) {
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
final PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setFeedItemRead(played, itemId, mediaId,
@@ -746,7 +767,7 @@ public class DBWriter {
* @param feedId ID of the Feed.
*/
public static Future<?> removeFeedNewFlag(final long feedId) {
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
final PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setFeedItems(FeedItem.NEW, FeedItem.UNPLAYED, feedId);
@@ -760,7 +781,7 @@ public class DBWriter {
* Sets the 'read'-attribute of all NEW FeedItems to UNPLAYED.
*/
public static Future<?> removeAllNewFlags() {
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
final PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setFeedItems(FeedItem.NEW, FeedItem.UNPLAYED);
@@ -771,7 +792,7 @@ public class DBWriter {
}
static Future<?> addNewFeed(final Context context, final Feed... feeds) {
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
final PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setCompleteFeed(feeds);
@@ -789,7 +810,7 @@ public class DBWriter {
}
static Future<?> setCompleteFeed(final Feed... feeds) {
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setCompleteFeed(feeds);
@@ -798,7 +819,7 @@ public class DBWriter {
}
public static Future<?> setItemList(final List<FeedItem> items) {
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.storeFeedItemlist(items);
@@ -814,7 +835,7 @@ public class DBWriter {
* @param media The FeedMedia object.
*/
public static Future<?> setFeedMedia(final FeedMedia media) {
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setMedia(media);
@@ -828,7 +849,7 @@ public class DBWriter {
* @param media The FeedMedia object.
*/
public static Future<?> setFeedMediaPlaybackInformation(final FeedMedia media) {
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setFeedMediaPlaybackInformation(media);
@@ -843,7 +864,7 @@ public class DBWriter {
* @param item The FeedItem object.
*/
public static Future<?> setFeedItem(final FeedItem item) {
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setSingleFeedItem(item);
@@ -857,7 +878,7 @@ public class DBWriter {
*/
public static Future<?> updateFeedDownloadURL(final String original, final String updated) {
Log.d(TAG, "updateFeedDownloadURL(original: " + original + ", updated: " + updated + ")");
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setFeedDownloadUrl(original, updated);
@@ -871,7 +892,7 @@ public class DBWriter {
* @param preferences The FeedPreferences object.
*/
public static Future<?> setFeedPreferences(final FeedPreferences preferences) {
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setFeedPreferences(preferences);
@@ -901,7 +922,7 @@ public class DBWriter {
*/
public static Future<?> setFeedLastUpdateFailed(final long feedId,
final boolean lastUpdateFailed) {
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setFeedLastUpdateFailed(feedId, lastUpdateFailed);
@@ -911,7 +932,7 @@ public class DBWriter {
}
public static Future<?> setFeedCustomTitle(Feed feed) {
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setFeedCustomTitle(feed.getId(), feed.getCustomTitle());
@@ -930,10 +951,10 @@ public class DBWriter {
public static Future<?> reorderQueue(@Nullable SortOrder sortOrder, final boolean broadcastUpdate) {
if (sortOrder == null) {
Log.w(TAG, "reorderQueue() - sortOrder is null. Do nothing.");
- return dbExec.submit(() -> { });
+ return runOnDbThread(() -> { });
}
final Permutor<FeedItem> permutor = FeedItemPermutors.getPermutor(sortOrder);
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
final PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
final List<FeedItem> queue = DBReader.getQueue(adapter);
@@ -960,7 +981,7 @@ public class DBWriter {
public static Future<?> setFeedItemsFilter(final long feedId,
final Set<String> filterValues) {
Log.d(TAG, "setFeedItemsFilter() called with: " + "feedId = [" + feedId + "], filterValues = [" + filterValues + "]");
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setFeedItemFilter(feedId, filterValues);
@@ -974,7 +995,7 @@ public class DBWriter {
*
*/
public static Future<?> setFeedItemSortOrder(long feedId, @Nullable SortOrder sortOrder) {
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setFeedItemSortOrder(feedId, sortOrder);
@@ -988,11 +1009,24 @@ public class DBWriter {
*/
@NonNull
public static Future<?> resetStatistics() {
- return dbExec.submit(() -> {
+ return runOnDbThread(() -> {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.resetAllMediaPlayedDuration();
adapter.close();
});
}
+
+ /**
+ * Submit to the DB thread only if caller is not already on the DB thread. Otherwise,
+ * just execute synchronously
+ */
+ private static Future<?> runOnDbThread(Runnable runnable) {
+ if ("DatabaseExecutor".equals(Thread.currentThread().getName())) {
+ runnable.run();
+ return Futures.immediateFuture(null);
+ } else {
+ return dbExec.submit(runnable);
+ }
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/FeedUtil.java b/core/src/main/java/de/danoeh/antennapod/core/util/FeedUtil.java
new file mode 100644
index 000000000..201207816
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/FeedUtil.java
@@ -0,0 +1,13 @@
+package de.danoeh.antennapod.core.util;
+
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.storage.preferences.UserPreferences;
+
+public abstract class FeedUtil {
+ public static boolean shouldAutoDeleteItemsOnThatFeed(Feed feed) {
+ if (!UserPreferences.isAutoDelete()) {
+ return false;
+ }
+ return !feed.isLocalFeed() || UserPreferences.isAutoDeleteLocal();
+ }
+}
diff --git a/storage/preferences/src/main/java/de/danoeh/antennapod/storage/preferences/UserPreferences.java b/storage/preferences/src/main/java/de/danoeh/antennapod/storage/preferences/UserPreferences.java
index c8ad3eec5..80a58525d 100644
--- a/storage/preferences/src/main/java/de/danoeh/antennapod/storage/preferences/UserPreferences.java
+++ b/storage/preferences/src/main/java/de/danoeh/antennapod/storage/preferences/UserPreferences.java
@@ -77,6 +77,7 @@ public class UserPreferences {
public static final String PREF_SKIP_KEEPS_EPISODE = "prefSkipKeepsEpisode";
private static final String PREF_FAVORITE_KEEPS_EPISODE = "prefFavoriteKeepsEpisode";
private static final String PREF_AUTO_DELETE = "prefAutoDelete";
+ private static final String PREF_AUTO_DELETE_LOCAL = "prefAutoDeleteLocal";
public static final String PREF_SMART_MARK_AS_PLAYED_SECS = "prefSmartMarkAsPlayedSecs";
private static final String PREF_PLAYBACK_SPEED_ARRAY = "prefPlaybackSpeedArray";
public static final String PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS = "prefPauseForFocusLoss";
@@ -366,6 +367,10 @@ public class UserPreferences {
return prefs.getBoolean(PREF_AUTO_DELETE, false);
}
+ public static boolean isAutoDeleteLocal() {
+ return prefs.getBoolean(PREF_AUTO_DELETE_LOCAL, false);
+ }
+
public static int getSmartMarkAsPlayedSecs() {
return Integer.parseInt(prefs.getString(PREF_SMART_MARK_AS_PLAYED_SECS, "30"));
}
diff --git a/ui/i18n/src/main/res/values/strings.xml b/ui/i18n/src/main/res/values/strings.xml
index 585701a9f..c585a6616 100644
--- a/ui/i18n/src/main/res/values/strings.xml
+++ b/ui/i18n/src/main/res/values/strings.xml
@@ -216,6 +216,7 @@
<string name="stream_label">Stream</string>
<string name="delete_label">Delete</string>
<string name="delete_failed">Unable to delete file. Rebooting the device could help.</string>
+ <string name="delete_local_failed">Unable to delete file. Try re-connecting the local folder from the podcast info screen.</string>
<string name="delete_episode_label">Delete episode</string>
<plurals name="deleted_multi_episode_batch_label">
<item quantity="one">1 downloaded episode deleted.</item>
@@ -259,6 +260,7 @@
<string name="skip_episode_label">Skip episode</string>
<string name="reset_position">Reset playback position</string>
<string name="no_items_selected">No items selected</string>
+ <string name="delete_local_feed_warning_body">Deleting removes the episode from AntennaPod and deletes the media file from your device storage. It cannot be downloaded again through AntennaPod.</string>
<!-- Download messages and labels -->
<string name="download_successful">successful</string>
@@ -401,6 +403,9 @@
<string name="pref_followQueue_sum">Jump to next queue item when playback completes</string>
<string name="pref_auto_delete_sum">Delete episode when playback completes</string>
<string name="pref_auto_delete_title">Auto delete</string>
+ <string name="pref_auto_local_delete_title">Auto delete from local folders</string>
+ <string name="pref_auto_local_delete_sum">Include local folders in Auto delete functionality</string>
+ <string name="pref_auto_local_delete_dialog_body">Note that for local folders this will remove episodes from AntennaPod and delete their media files from your device storage. They cannot be downloaded again through AntennaPod. Enable auto delete?</string>
<string name="pref_smart_mark_as_played_sum">Mark episodes as played even if less than a certain amount of seconds of playing time is still left</string>
<string name="pref_smart_mark_as_played_title">Smart mark as played</string>
<string name="pref_skip_keeps_episodes_sum">Keep episodes when they are skipped</string>