From 084723ad76f93822b9a15b24bb55a1fefa155dce Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Thu, 9 May 2024 11:44:26 +0200 Subject: [PATCH] Add episodes without subscribing (#7098) --- .../actionbutton/ItemActionButton.java | 3 + .../episodeslist/EpisodeItemListAdapter.java | 20 +- .../episodeslist/EpisodeItemViewHolder.java | 6 +- .../EpisodeMultiSelectActionHandler.java | 18 +- .../ui/episodeslist/FeedItemMenuHandler.java | 11 +- .../ui/screen/AllEpisodesFragment.java | 8 +- .../ui/screen/PlaybackHistoryFragment.java | 3 +- .../download/CompletedDownloadsFragment.java | 2 +- .../ui/screen/episode/ItemFragment.java | 18 +- .../ui/screen/episode/ItemPagerFragment.java | 12 +- .../ui/screen/feed/FeedInfoFragment.java | 29 +- .../ui/screen/feed/FeedItemlistFragment.java | 163 ++++---- .../home/sections/DownloadsSection.java | 7 +- .../FeedItemlistDescriptionAdapter.java | 112 ------ .../OnlineFeedViewActivity.java | 365 ++++-------------- .../playback/audio/AudioPlayerFragment.java | 17 +- .../screen/playback/audio/CoverFragment.java | 18 +- .../layout/alternate_urls_dropdown_item.xml | 8 - .../main/res/layout/alternate_urls_item.xml | 9 - app/src/main/res/layout/feedinfo.xml | 6 +- app/src/main/res/layout/feeditem_fragment.xml | 14 + .../main/res/layout/feeditemlist_header.xml | 56 +++ .../res/layout/itemdescription_listitem.xml | 51 --- .../res/layout/onlinefeedview_activity.xml | 170 +------- .../main/res/layout/onlinefeedview_header.xml | 36 -- .../de/danoeh/antennapod/model/feed/Feed.java | 16 +- .../antennapod/model/feed/FeedItemFilter.java | 10 + .../model/feed/SubscriptionsFilter.java | 9 +- .../net/discovery/ItunesTopListLoader.java | 3 +- .../episode/MediaDownloadedHandler.java | 3 +- .../service/feed/FeedUpdateWorker.java | 6 +- .../SynchronizationQueueSink.java | 4 +- .../playback/service/PlaybackService.java | 4 +- .../antennapod/storage/database/DBReader.java | 9 +- .../storage/database/DBUpgrader.java | 5 + .../antennapod/storage/database/DBWriter.java | 44 ++- .../storage/database/FeedDatabaseWriter.java | 3 +- .../database/NonSubscribedFeedsCleaner.java | 46 +++ .../storage/database/PodDBAdapter.java | 30 +- .../storage/database/mapper/FeedCursor.java | 5 +- .../database/mapper/FeedItemFilterQuery.java | 3 + .../NonSubscribedFeedsCleanerTest.java | 104 +++++ .../storage/importexport/HtmlWriter.java | 3 + .../storage/importexport/OpmlWriter.java | 3 + .../src/main/res/drawable/bg_message_info.xml | 17 + .../main/res/drawable/bg_rounded_corners.xml | 6 - ui/common/src/main/res/drawable/ic_close.xml | 11 + ui/common/src/main/res/values/colors.xml | 1 - ui/common/src/main/res/values/dimens.xml | 2 - ui/i18n/src/main/res/values/strings.xml | 5 +- 50 files changed, 687 insertions(+), 827 deletions(-) delete mode 100644 app/src/main/java/de/danoeh/antennapod/ui/screen/onlinefeedview/FeedItemlistDescriptionAdapter.java delete mode 100644 app/src/main/res/layout/alternate_urls_dropdown_item.xml delete mode 100644 app/src/main/res/layout/alternate_urls_item.xml delete mode 100644 app/src/main/res/layout/itemdescription_listitem.xml delete mode 100644 app/src/main/res/layout/onlinefeedview_header.xml create mode 100644 storage/database/src/main/java/de/danoeh/antennapod/storage/database/NonSubscribedFeedsCleaner.java create mode 100644 storage/database/src/test/java/de/danoeh/antennapod/storage/database/NonSubscribedFeedsCleanerTest.java create mode 100644 ui/common/src/main/res/drawable/bg_message_info.xml delete mode 100644 ui/common/src/main/res/drawable/bg_rounded_corners.xml create mode 100644 ui/common/src/main/res/drawable/ic_close.xml diff --git a/app/src/main/java/de/danoeh/antennapod/actionbutton/ItemActionButton.java b/app/src/main/java/de/danoeh/antennapod/actionbutton/ItemActionButton.java index 84b246167..54714be3d 100644 --- a/app/src/main/java/de/danoeh/antennapod/actionbutton/ItemActionButton.java +++ b/app/src/main/java/de/danoeh/antennapod/actionbutton/ItemActionButton.java @@ -7,6 +7,7 @@ import androidx.annotation.NonNull; import androidx.annotation.StringRes; import android.view.View; +import de.danoeh.antennapod.model.feed.Feed; import de.danoeh.antennapod.playback.service.PlaybackStatus; import de.danoeh.antennapod.model.feed.FeedItem; import de.danoeh.antennapod.model.feed.FeedMedia; @@ -48,6 +49,8 @@ public abstract class ItemActionButton { return new PlayActionButton(item); } else if (isDownloadingMedia) { return new CancelDownloadActionButton(item); + } else if (item.getFeed().getState() != Feed.STATE_SUBSCRIBED) { + return new StreamActionButton(item); } else if (UserPreferences.isStreamOverDownload()) { return new StreamActionButton(item); } else { diff --git a/app/src/main/java/de/danoeh/antennapod/ui/episodeslist/EpisodeItemListAdapter.java b/app/src/main/java/de/danoeh/antennapod/ui/episodeslist/EpisodeItemListAdapter.java index 5082f25ff..db7991261 100644 --- a/app/src/main/java/de/danoeh/antennapod/ui/episodeslist/EpisodeItemListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/ui/episodeslist/EpisodeItemListAdapter.java @@ -12,6 +12,7 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.fragment.app.FragmentActivity; import androidx.recyclerview.widget.RecyclerView; import de.danoeh.antennapod.ui.SelectableAdapter; @@ -32,13 +33,13 @@ import de.danoeh.antennapod.ui.screen.episode.ItemPagerFragment; public class EpisodeItemListAdapter extends SelectableAdapter implements View.OnCreateContextMenuListener { - private final WeakReference mainActivityRef; + private final WeakReference mainActivityRef; private List episodes = new ArrayList<>(); private FeedItem longPressedItem; int longPressedPosition = 0; // used to init actionMode private int dummyViews = 0; - public EpisodeItemListAdapter(MainActivity mainActivity) { + public EpisodeItemListAdapter(FragmentActivity mainActivity) { super(mainActivity); this.mainActivityRef = new WeakReference<>(mainActivity); setHasStableIds(true); @@ -86,9 +87,18 @@ public class EpisodeItemListAdapter extends SelectableAdapter { - MainActivity activity = mainActivityRef.get(); - if (activity != null && !inActionMode()) { - activity.loadChildFragment(ItemPagerFragment.newInstance(episodes, item)); + if (!inActionMode()) { + if (mainActivityRef.get() instanceof MainActivity) { + ((MainActivity) mainActivityRef.get()) + .loadChildFragment(ItemPagerFragment.newInstance(episodes, item)); + } else { + ItemPagerFragment fragment = ItemPagerFragment.newInstance(episodes, item); + mainActivityRef.get().getSupportFragmentManager() + .beginTransaction() + .replace(R.id.fragmentContainer, fragment, "Items") + .addToBackStack("Items") + .commitAllowingStateLoss(); + } } else { toggleSelection(holder.getBindingAdapterPosition()); } diff --git a/app/src/main/java/de/danoeh/antennapod/ui/episodeslist/EpisodeItemViewHolder.java b/app/src/main/java/de/danoeh/antennapod/ui/episodeslist/EpisodeItemViewHolder.java index dd1a10450..c5868a945 100644 --- a/app/src/main/java/de/danoeh/antennapod/ui/episodeslist/EpisodeItemViewHolder.java +++ b/app/src/main/java/de/danoeh/antennapod/ui/episodeslist/EpisodeItemViewHolder.java @@ -1,5 +1,6 @@ package de.danoeh.antennapod.ui.episodeslist; +import android.app.Activity; import android.os.Build; import android.text.Layout; import android.text.format.Formatter; @@ -17,7 +18,6 @@ import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.elevation.SurfaceColors; import de.danoeh.antennapod.R; -import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.ui.CoverLoader; import de.danoeh.antennapod.actionbutton.ItemActionButton; import de.danoeh.antennapod.playback.service.PlaybackStatus; @@ -62,10 +62,10 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder { private final View leftPadding; public final CardView coverHolder; - private final MainActivity activity; + private final Activity activity; private FeedItem item; - public EpisodeItemViewHolder(MainActivity activity, ViewGroup parent) { + public EpisodeItemViewHolder(Activity activity, ViewGroup parent) { super(LayoutInflater.from(activity).inflate(R.layout.feeditemlist_item, parent, false)); this.activity = activity; container = itemView.findViewById(R.id.container); diff --git a/app/src/main/java/de/danoeh/antennapod/ui/episodeslist/EpisodeMultiSelectActionHandler.java b/app/src/main/java/de/danoeh/antennapod/ui/episodeslist/EpisodeMultiSelectActionHandler.java index 76ed47de5..a655982cb 100644 --- a/app/src/main/java/de/danoeh/antennapod/ui/episodeslist/EpisodeMultiSelectActionHandler.java +++ b/app/src/main/java/de/danoeh/antennapod/ui/episodeslist/EpisodeMultiSelectActionHandler.java @@ -1,29 +1,28 @@ package de.danoeh.antennapod.ui.episodeslist; +import android.app.Activity; import android.util.Log; import androidx.annotation.PluralsRes; -import com.google.android.material.snackbar.Snackbar; - import java.util.List; import de.danoeh.antennapod.R; -import de.danoeh.antennapod.activity.MainActivity; +import de.danoeh.antennapod.event.MessageEvent; import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface; import de.danoeh.antennapod.storage.database.DBWriter; import de.danoeh.antennapod.storage.database.LongList; import de.danoeh.antennapod.model.feed.FeedItem; import de.danoeh.antennapod.ui.view.LocalDeleteModal; +import org.greenrobot.eventbus.EventBus; public class EpisodeMultiSelectActionHandler { private static final String TAG = "EpisodeSelectHandler"; - private final MainActivity activity; + private final Activity activity; private final int actionId; private int totalNumItems = 0; - private Snackbar snackbar = null; - public EpisodeMultiSelectActionHandler(MainActivity activity, int actionId) { + public EpisodeMultiSelectActionHandler(Activity activity, int actionId) { this.activity = activity; this.actionId = actionId; } @@ -116,12 +115,7 @@ public class EpisodeMultiSelectActionHandler { totalNumItems += numItems; activity.runOnUiThread(() -> { String text = activity.getResources().getQuantityString(msgId, totalNumItems, totalNumItems); - if (snackbar != null) { - snackbar.setText(text); - snackbar.show(); // Resets the timeout - } else { - snackbar = activity.showSnackbarAbovePlayer(text, Snackbar.LENGTH_LONG); - } + EventBus.getDefault().post(new MessageEvent(text)); }); } diff --git a/app/src/main/java/de/danoeh/antennapod/ui/episodeslist/FeedItemMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/ui/episodeslist/FeedItemMenuHandler.java index e1974940c..720e0ccbc 100644 --- a/app/src/main/java/de/danoeh/antennapod/ui/episodeslist/FeedItemMenuHandler.java +++ b/app/src/main/java/de/danoeh/antennapod/ui/episodeslist/FeedItemMenuHandler.java @@ -16,6 +16,7 @@ import java.util.Arrays; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; +import de.danoeh.antennapod.model.feed.Feed; import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueueSink; import de.danoeh.antennapod.storage.preferences.PlaybackPreferences; import de.danoeh.antennapod.playback.service.PlaybackServiceInterface; @@ -84,6 +85,10 @@ 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 || isLocalFile); + + if (selectedItem.getFeed().getState() != Feed.STATE_SUBSCRIBED) { + setItemVisibility(menu, R.id.mark_read_item, false); + } return true; } @@ -158,7 +163,8 @@ public class FeedItemMenuHandler { } else if (menuItemId == R.id.mark_read_item) { selectedItem.setPlayed(true); DBWriter.markItemPlayed(selectedItem, FeedItem.PLAYED, true); - if (!selectedItem.getFeed().isLocalFeed() && SynchronizationSettings.isProviderConnected()) { + if (!selectedItem.getFeed().isLocalFeed() && selectedItem.getFeed().getState() == Feed.STATE_SUBSCRIBED + && SynchronizationSettings.isProviderConnected()) { FeedMedia media = selectedItem.getMedia(); // not all items have media, Gpodder only cares about those that do if (media != null) { @@ -174,7 +180,8 @@ public class FeedItemMenuHandler { } else if (menuItemId == R.id.mark_unread_item) { selectedItem.setPlayed(false); DBWriter.markItemPlayed(selectedItem, FeedItem.UNPLAYED, false); - if (!selectedItem.getFeed().isLocalFeed() && selectedItem.getMedia() != null) { + if (!selectedItem.getFeed().isLocalFeed() && selectedItem.getMedia() != null + && selectedItem.getFeed().getState() == Feed.STATE_SUBSCRIBED) { EpisodeAction actionNew = new EpisodeAction.Builder(selectedItem, EpisodeAction.NEW) .currentTimestamp() .build(); diff --git a/app/src/main/java/de/danoeh/antennapod/ui/screen/AllEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/ui/screen/AllEpisodesFragment.java index eb49c94d9..6e48ade39 100644 --- a/app/src/main/java/de/danoeh/antennapod/ui/screen/AllEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/ui/screen/AllEpisodesFragment.java @@ -66,7 +66,12 @@ public class AllEpisodesFragment extends EpisodesListFragment { @Override protected FeedItemFilter getFilter() { - return new FeedItemFilter(UserPreferences.getPrefFilterAllEpisodes()); + FeedItemFilter filter = new FeedItemFilter(UserPreferences.getPrefFilterAllEpisodes()); + if (filter.showIsFavorite) { + return new FeedItemFilter(filter, FeedItemFilter.INCLUDE_NOT_SUBSCRIBED); + } else { + return filter; + } } @Override @@ -91,6 +96,7 @@ public class AllEpisodesFragment extends EpisodesListFragment { ArrayList filter = new ArrayList<>(getFilter().getValuesList()); if (filter.contains(FeedItemFilter.IS_FAVORITE)) { filter.remove(FeedItemFilter.IS_FAVORITE); + filter.remove(FeedItemFilter.INCLUDE_NOT_SUBSCRIBED); } else { filter.add(FeedItemFilter.IS_FAVORITE); } diff --git a/app/src/main/java/de/danoeh/antennapod/ui/screen/PlaybackHistoryFragment.java b/app/src/main/java/de/danoeh/antennapod/ui/screen/PlaybackHistoryFragment.java index a02f86bd2..abdf043b1 100644 --- a/app/src/main/java/de/danoeh/antennapod/ui/screen/PlaybackHistoryFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/ui/screen/PlaybackHistoryFragment.java @@ -23,7 +23,8 @@ import java.util.List; public class PlaybackHistoryFragment extends EpisodesListFragment { public static final String TAG = "PlaybackHistoryFragment"; - private static final FeedItemFilter FILTER_HISTORY = new FeedItemFilter(FeedItemFilter.IS_IN_HISTORY); + private static final FeedItemFilter FILTER_HISTORY = new FeedItemFilter( + FeedItemFilter.IS_IN_HISTORY, FeedItemFilter.INCLUDE_NOT_SUBSCRIBED); @NonNull @Override diff --git a/app/src/main/java/de/danoeh/antennapod/ui/screen/download/CompletedDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/ui/screen/download/CompletedDownloadsFragment.java index 001c2b465..0d82b7133 100644 --- a/app/src/main/java/de/danoeh/antennapod/ui/screen/download/CompletedDownloadsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/ui/screen/download/CompletedDownloadsFragment.java @@ -285,7 +285,7 @@ public class CompletedDownloadsFragment extends Fragment disposable = Observable.fromCallable(() -> { SortOrder sortOrder = UserPreferences.getDownloadsSortedOrder(); List downloadedItems = DBReader.getEpisodes(0, Integer.MAX_VALUE, - new FeedItemFilter(FeedItemFilter.DOWNLOADED), sortOrder); + new FeedItemFilter(FeedItemFilter.DOWNLOADED, FeedItemFilter.INCLUDE_NOT_SUBSCRIBED), sortOrder); List mediaUrls = new ArrayList<>(); if (runningDownloads == null) { diff --git a/app/src/main/java/de/danoeh/antennapod/ui/screen/episode/ItemFragment.java b/app/src/main/java/de/danoeh/antennapod/ui/screen/episode/ItemFragment.java index e31004a57..a5ee78a58 100644 --- a/app/src/main/java/de/danoeh/antennapod/ui/screen/episode/ItemFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/ui/screen/episode/ItemFragment.java @@ -38,6 +38,7 @@ import de.danoeh.antennapod.actionbutton.PlayLocalActionButton; import de.danoeh.antennapod.actionbutton.StreamActionButton; import de.danoeh.antennapod.actionbutton.VisitWebsiteActionButton; import de.danoeh.antennapod.event.EpisodeDownloadEvent; +import de.danoeh.antennapod.model.feed.Feed; import de.danoeh.antennapod.playback.service.PlaybackStatus; import de.danoeh.antennapod.event.FeedItemEvent; import de.danoeh.antennapod.event.PlayerStatusEvent; @@ -49,6 +50,7 @@ import de.danoeh.antennapod.storage.preferences.UsageStatistics; import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface; import de.danoeh.antennapod.storage.preferences.UserPreferences; import de.danoeh.antennapod.storage.database.DBReader; +import de.danoeh.antennapod.ui.appstartintent.OnlineFeedviewActivityStarter; import de.danoeh.antennapod.ui.common.Converter; import de.danoeh.antennapod.ui.common.DateFormatter; import de.danoeh.antennapod.ui.common.CircularProgressBar; @@ -114,6 +116,7 @@ public class ItemFragment extends Fragment { private ItemActionButton actionButton1; private ItemActionButton actionButton2; private View noMediaLabel; + private View nonSubscribedWarningLabel; private Disposable disposable; private PlaybackController controller; @@ -164,6 +167,7 @@ public class ItemFragment extends Fragment { butAction1Text = layout.findViewById(R.id.butAction1Text); butAction2Text = layout.findViewById(R.id.butAction2Text); noMediaLabel = layout.findViewById(R.id.noMediaLabel); + nonSubscribedWarningLabel = layout.findViewById(R.id.nonSubscribedWarningLabel); butAction1.setOnClickListener(v -> { if (actionButton1 instanceof StreamActionButton && !UserPreferences.isStreamOverDownload() @@ -287,6 +291,11 @@ public class ItemFragment extends Fragment { txtvPublished.setContentDescription(DateFormatter.formatForAccessibility(item.getPubDate())); } + if (item.getFeed().getState() != Feed.STATE_SUBSCRIBED) { + nonSubscribedWarningLabel.setVisibility(View.VISIBLE); + nonSubscribedWarningLabel.setOnClickListener(v -> openPodcast()); + } + float radius = 8 * getResources().getDisplayMetrics().density; RequestOptions options = new RequestOptions() .error(ImagePlaceholder.getDrawable(getContext(), radius)) @@ -366,8 +375,13 @@ public class ItemFragment extends Fragment { if (item == null) { return; } - Fragment fragment = FeedItemlistFragment.newInstance(item.getFeedId()); - ((MainActivity) getActivity()).loadChildFragment(fragment); + if (item.getFeed().getState() == Feed.STATE_SUBSCRIBED) { + Fragment fragment = FeedItemlistFragment.newInstance(item.getFeedId()); + ((MainActivity) getActivity()).loadChildFragment(fragment); + } else { + startActivity(new OnlineFeedviewActivityStarter(getContext(), item.getFeed().getDownloadUrl()) + .getIntent()); + } } @Subscribe(threadMode = ThreadMode.MAIN) diff --git a/app/src/main/java/de/danoeh/antennapod/ui/screen/episode/ItemPagerFragment.java b/app/src/main/java/de/danoeh/antennapod/ui/screen/episode/ItemPagerFragment.java index e7f345617..951e5affb 100644 --- a/app/src/main/java/de/danoeh/antennapod/ui/screen/episode/ItemPagerFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/ui/screen/episode/ItemPagerFragment.java @@ -1,5 +1,6 @@ package de.danoeh.antennapod.ui.screen.episode; +import android.content.Intent; import android.os.Bundle; import android.view.LayoutInflater; import android.view.MenuItem; @@ -13,7 +14,8 @@ import androidx.fragment.app.Fragment; import androidx.viewpager2.adapter.FragmentStateAdapter; import androidx.viewpager2.widget.ViewPager2; -import de.danoeh.antennapod.ui.screen.feed.FeedItemlistFragment; +import de.danoeh.antennapod.model.feed.Feed; +import de.danoeh.antennapod.ui.appstartintent.OnlineFeedviewActivityStarter; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; @@ -173,8 +175,12 @@ public class ItemPagerFragment extends Fragment implements MaterialToolbar.OnMen if (item == null) { return; } - Fragment fragment = FeedItemlistFragment.newInstance(item.getFeedId()); - ((MainActivity) getActivity()).loadChildFragment(fragment); + if (item.getFeed().getState() == Feed.STATE_SUBSCRIBED) { + Intent intent = MainActivity.getIntentToOpenFeed(getContext(), item.getFeedId()); + startActivity(intent); + } else { + startActivity(new OnlineFeedviewActivityStarter(getContext(), item.getFeed().getDownloadUrl()).getIntent()); + } } private class ItemPagerAdapter extends FragmentStateAdapter { diff --git a/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/FeedInfoFragment.java b/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/FeedInfoFragment.java index aabb0852e..f73b69d75 100644 --- a/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/FeedInfoFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/FeedInfoFragment.java @@ -122,7 +122,6 @@ public class FeedInfoFragment extends Fragment implements MaterialToolbar.OnMenu viewBinding.header.butFilter.setVisibility(View.INVISIBLE); // https://github.com/bumptech/glide/issues/529 viewBinding.imgvBackground.setColorFilter(new LightingColorFilter(0xff828282, 0x000000)); - viewBinding.urlLabel.setOnClickListener(copyUrlToClipboard); long feedId = getArguments().getLong(EXTRA_FEED_ID); @@ -237,6 +236,25 @@ public class FeedInfoFragment extends Fragment implements MaterialToolbar.OnMenu viewBinding.supportUrl.setText(str.toString()); } + if (feed.getState() == Feed.STATE_SUBSCRIBED) { + long feedId = getArguments().getLong(EXTRA_FEED_ID); + getParentFragmentManager().beginTransaction().replace(R.id.statisticsFragmentContainer, + FeedStatisticsFragment.newInstance(feedId, false), "feed_statistics_fragment") + .commitAllowingStateLoss(); + + viewBinding.statisticsButton.setOnClickListener(view -> { + StatisticsFragment fragment = new StatisticsFragment(); + ((MainActivity) getActivity()).loadChildFragment(fragment, TransitionEffect.SLIDE); + }); + } else { + viewBinding.statisticsButton.setVisibility(View.GONE); + viewBinding.statisticsFragmentContainer.setVisibility(View.GONE); + viewBinding.statisticsHeadingLabel.setVisibility(View.GONE); + viewBinding.supportHeadingLabel.setVisibility(View.GONE); + viewBinding.supportUrl.setVisibility(View.GONE); + viewBinding.descriptionHeadingLabel.setVisibility(View.GONE); + } + refreshToolbarState(); } @@ -249,13 +267,14 @@ public class FeedInfoFragment extends Fragment implements MaterialToolbar.OnMenu } private void refreshToolbarState() { + boolean isSubscribed = feed != null && feed.getState() == Feed.STATE_SUBSCRIBED; viewBinding.toolbar.getMenu().findItem(R.id.reconnect_local_folder).setVisible( - feed != null && feed.isLocalFeed()); - viewBinding.toolbar.getMenu().findItem(R.id.share_item).setVisible(feed != null && !feed.isLocalFeed()); - viewBinding.toolbar.getMenu().findItem(R.id.visit_website_item).setVisible(feed != null + isSubscribed && feed.isLocalFeed()); + viewBinding.toolbar.getMenu().findItem(R.id.share_item).setVisible(isSubscribed && !feed.isLocalFeed()); + viewBinding.toolbar.getMenu().findItem(R.id.visit_website_item).setVisible(isSubscribed && feed.getLink() != null && IntentUtils.isCallable(getContext(), new Intent(Intent.ACTION_VIEW, Uri.parse(feed.getLink())))); - viewBinding.toolbar.getMenu().findItem(R.id.edit_feed_url_item).setVisible(feed != null && !feed.isLocalFeed()); + viewBinding.toolbar.getMenu().findItem(R.id.edit_feed_url_item).setVisible(isSubscribed && !feed.isLocalFeed()); } @Override diff --git a/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/FeedItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/FeedItemlistFragment.java index 3c6931794..fbfb9fa0a 100644 --- a/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/FeedItemlistFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/ui/screen/feed/FeedItemlistFragment.java @@ -12,27 +12,63 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; -import android.widget.Toast; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.content.res.AppCompatResources; import androidx.core.util.Pair; import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; import androidx.recyclerview.widget.RecyclerView; - import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestOptions; import com.google.android.material.appbar.MaterialToolbar; import com.google.android.material.snackbar.Snackbar; - -import de.danoeh.antennapod.ui.CoverLoader; -import de.danoeh.antennapod.ui.screen.episode.ItemPagerFragment; -import de.danoeh.antennapod.ui.screen.SearchFragment; -import de.danoeh.antennapod.ui.TransitionEffect; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.MainActivity; +import de.danoeh.antennapod.databinding.FeedItemListFragmentBinding; +import de.danoeh.antennapod.event.EpisodeDownloadEvent; +import de.danoeh.antennapod.event.FavoritesEvent; +import de.danoeh.antennapod.event.FeedEvent; +import de.danoeh.antennapod.event.FeedItemEvent; +import de.danoeh.antennapod.event.FeedListUpdateEvent; +import de.danoeh.antennapod.event.FeedUpdateRunningEvent; +import de.danoeh.antennapod.event.PlayerStatusEvent; +import de.danoeh.antennapod.event.QueueEvent; +import de.danoeh.antennapod.event.UnreadItemsUpdateEvent; +import de.danoeh.antennapod.event.playback.PlaybackPositionEvent; +import de.danoeh.antennapod.model.download.DownloadResult; +import de.danoeh.antennapod.model.feed.Feed; +import de.danoeh.antennapod.model.feed.FeedItem; +import de.danoeh.antennapod.model.feed.FeedItemFilter; import de.danoeh.antennapod.net.download.serviceinterface.FeedUpdateManager; +import de.danoeh.antennapod.storage.database.DBReader; +import de.danoeh.antennapod.storage.database.DBWriter; +import de.danoeh.antennapod.storage.preferences.UserPreferences; +import de.danoeh.antennapod.ui.CoverLoader; +import de.danoeh.antennapod.ui.FeedItemFilterDialog; +import de.danoeh.antennapod.ui.MenuItemUtils; +import de.danoeh.antennapod.ui.TransitionEffect; +import de.danoeh.antennapod.ui.appstartintent.MainActivityStarter; +import de.danoeh.antennapod.ui.cleaner.HtmlToPlainText; +import de.danoeh.antennapod.ui.common.IntentUtils; +import de.danoeh.antennapod.ui.episodeslist.EpisodeItemListAdapter; +import de.danoeh.antennapod.ui.episodeslist.EpisodeItemViewHolder; +import de.danoeh.antennapod.ui.episodeslist.EpisodeMultiSelectActionHandler; +import de.danoeh.antennapod.ui.episodeslist.FeedItemMenuHandler; +import de.danoeh.antennapod.ui.episodeslist.MoreContentListFooterUtil; +import de.danoeh.antennapod.ui.glide.FastBlurTransformation; +import de.danoeh.antennapod.ui.screen.SearchFragment; +import de.danoeh.antennapod.ui.screen.download.DownloadLogDetailsDialog; import de.danoeh.antennapod.ui.screen.download.DownloadLogFragment; +import de.danoeh.antennapod.ui.screen.episode.ItemPagerFragment; import de.danoeh.antennapod.ui.screen.feed.preferences.FeedSettingsFragment; +import de.danoeh.antennapod.ui.share.ShareUtils; +import de.danoeh.antennapod.ui.swipeactions.SwipeActions; +import io.reactivex.Maybe; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.greenrobot.eventbus.EventBus; @@ -43,44 +79,6 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutionException; -import de.danoeh.antennapod.R; -import de.danoeh.antennapod.activity.MainActivity; -import de.danoeh.antennapod.ui.episodeslist.EpisodeItemListAdapter; -import de.danoeh.antennapod.event.FeedEvent; -import de.danoeh.antennapod.ui.MenuItemUtils; -import de.danoeh.antennapod.storage.database.DBReader; -import de.danoeh.antennapod.storage.database.DBWriter; -import de.danoeh.antennapod.ui.common.IntentUtils; -import de.danoeh.antennapod.ui.share.ShareUtils; -import de.danoeh.antennapod.ui.episodeslist.MoreContentListFooterUtil; -import de.danoeh.antennapod.databinding.FeedItemListFragmentBinding; -import de.danoeh.antennapod.ui.screen.download.DownloadLogDetailsDialog; -import de.danoeh.antennapod.ui.FeedItemFilterDialog; -import de.danoeh.antennapod.event.EpisodeDownloadEvent; -import de.danoeh.antennapod.event.FavoritesEvent; -import de.danoeh.antennapod.event.FeedItemEvent; -import de.danoeh.antennapod.event.FeedListUpdateEvent; -import de.danoeh.antennapod.event.FeedUpdateRunningEvent; -import de.danoeh.antennapod.event.PlayerStatusEvent; -import de.danoeh.antennapod.event.QueueEvent; -import de.danoeh.antennapod.event.UnreadItemsUpdateEvent; -import de.danoeh.antennapod.event.playback.PlaybackPositionEvent; -import de.danoeh.antennapod.ui.episodeslist.EpisodeMultiSelectActionHandler; -import de.danoeh.antennapod.ui.swipeactions.SwipeActions; -import de.danoeh.antennapod.ui.episodeslist.FeedItemMenuHandler; -import de.danoeh.antennapod.model.download.DownloadResult; -import de.danoeh.antennapod.model.feed.Feed; -import de.danoeh.antennapod.model.feed.FeedItem; -import de.danoeh.antennapod.model.feed.FeedItemFilter; -import de.danoeh.antennapod.storage.preferences.UserPreferences; -import de.danoeh.antennapod.ui.glide.FastBlurTransformation; -import de.danoeh.antennapod.ui.episodeslist.EpisodeItemViewHolder; -import io.reactivex.Maybe; -import io.reactivex.Observable; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.Disposable; -import io.reactivex.schedulers.Schedulers; - /** * Displays a list of FeedItems. */ @@ -100,7 +98,6 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem private boolean displayUpArrow; private long feedID; private Feed feed; - private boolean headerCreated = false; private Disposable disposable; private FeedItemListFragmentBinding viewBinding; @@ -145,12 +142,18 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem if (savedInstanceState != null) { displayUpArrow = savedInstanceState.getBoolean(KEY_UP_ARROW); } - ((MainActivity) getActivity()).setupToolbarToggle(viewBinding.toolbar, displayUpArrow); + if (getActivity() instanceof MainActivity) { + ((MainActivity) getActivity()).setupToolbarToggle(viewBinding.toolbar, displayUpArrow); + viewBinding.recyclerView.setRecycledViewPool(((MainActivity) getActivity()).getRecycledViewPool()); + } else { + viewBinding.toolbar.setNavigationIcon(R.drawable.ic_close); + viewBinding.toolbar.setNavigationOnClickListener(view -> getActivity().finish()); + } updateToolbar(); setupLoadMoreScrollListener(); + setupHeaderView(); - viewBinding.recyclerView.setRecycledViewPool(((MainActivity) getActivity()).getRecycledViewPool()); - adapter = new FeedItemListAdapter((MainActivity) getActivity()); + adapter = new FeedItemListAdapter(getActivity()); adapter.setOnSelectModeListener(this); viewBinding.recyclerView.setAdapter(adapter); swipeActions = new SwipeActions(this, TAG).attachTo(viewBinding.recyclerView); @@ -198,7 +201,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem Snackbar.LENGTH_SHORT); return false; } - new EpisodeMultiSelectActionHandler(((MainActivity) getActivity()), menuItem.getItemId()) + new EpisodeMultiSelectActionHandler(getActivity(), menuItem.getItemId()) .handleAction(adapter.getSelectedItems()); adapter.endSelectMode(); return true; @@ -249,6 +252,13 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem if (feed.isLocalFeed()) { viewBinding.toolbar.getMenu().findItem(R.id.share_item).setVisible(false); } + if (feed.getState() == Feed.STATE_NOT_SUBSCRIBED) { + viewBinding.toolbar.getMenu().findItem(R.id.sort_items).setVisible(false); + viewBinding.toolbar.getMenu().findItem(R.id.refresh_item).setVisible(false); + viewBinding.toolbar.getMenu().findItem(R.id.rename_item).setVisible(false); + viewBinding.toolbar.getMenu().findItem(R.id.remove_feed).setVisible(false); + viewBinding.toolbar.getMenu().findItem(R.id.action_search).setVisible(false); + } } @Override @@ -263,8 +273,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem @Override public boolean onMenuItemClick(MenuItem item) { if (feed == null) { - ((MainActivity) getActivity()).showSnackbarAbovePlayer( - R.string.please_wait_for_data, Toast.LENGTH_LONG); + EventBus.getDefault().post(getString(R.string.please_wait_for_data)); return true; } if (item.getItemId() == R.id.visit_website_item) { @@ -443,7 +452,6 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem } private void refreshHeaderView() { - setupHeaderView(); if (viewBinding == null || feed == null) { Log.e(TAG, "Unable to refresh header view"); return; @@ -454,7 +462,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem } else { viewBinding.header.txtvFailure.setVisibility(View.GONE); } - if (!feed.getPreferences().getKeepUpdated()) { + if (!feed.getPreferences().getKeepUpdated() && feed.getState() == Feed.STATE_SUBSCRIBED) { viewBinding.header.txtvUpdatesDisabled.setText(R.string.updates_disabled_label); viewBinding.header.txtvUpdatesDisabled.setVisibility(View.VISIBLE); } else { @@ -462,7 +470,11 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem } viewBinding.header.txtvTitle.setText(feed.getTitle()); viewBinding.header.txtvAuthor.setText(feed.getAuthor()); - if (feed.getItemFilter() != null) { + viewBinding.header.descriptionContainer.setVisibility(View.GONE); + if (feed.getState() != Feed.STATE_SUBSCRIBED) { + viewBinding.header.descriptionContainer.setVisibility(View.VISIBLE); + viewBinding.header.headerDescriptionLabel.setText(HtmlToPlainText.getPlainText(feed.getDescription())); + } else if (feed.getItemFilter() != null) { FeedItemFilter filter = feed.getItemFilter(); if (filter.getValues().length > 0) { viewBinding.header.txtvInformation.setText(R.string.filtered_label); @@ -475,17 +487,30 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem } else { viewBinding.header.txtvInformation.setVisibility(View.GONE); } + boolean isSubscribed = feed.getState() == Feed.STATE_SUBSCRIBED; + viewBinding.header.butShowInfo.setVisibility(isSubscribed ? View.VISIBLE : View.GONE); + viewBinding.header.butFilter.setVisibility(isSubscribed ? View.VISIBLE : View.GONE); + viewBinding.header.butShowSettings.setVisibility(isSubscribed ? View.VISIBLE : View.GONE); + viewBinding.header.butSubscribe.setVisibility(isSubscribed ? View.GONE : View.VISIBLE); + + if (!isSubscribed && feed.getLastRefreshAttempt() < System.currentTimeMillis() - 1000L * 3600 * 24) { + FeedUpdateManager.getInstance().runOnce(getContext(), feed, true); + } } private void setupHeaderView() { - if (feed == null || headerCreated) { - return; - } - // https://github.com/bumptech/glide/issues/529 viewBinding.imgvBackground.setColorFilter(new LightingColorFilter(0xff666666, 0x000000)); viewBinding.header.butShowInfo.setOnClickListener(v -> showFeedInfo()); viewBinding.header.imgvCover.setOnClickListener(v -> showFeedInfo()); + viewBinding.header.headerDescriptionLabel.setOnClickListener(v -> showFeedInfo()); + viewBinding.header.butSubscribe.setOnClickListener(view -> { + DBWriter.setFeedState(getContext(), feed, Feed.STATE_SUBSCRIBED); + MainActivityStarter mainActivityStarter = new MainActivityStarter(getContext()); + mainActivityStarter.withOpenFeed(feed.getId()); + getActivity().finish(); + startActivity(mainActivityStarter.getIntent()); + }); viewBinding.header.butShowSettings.setOnClickListener(v -> { if (feed != null) { FeedSettingsFragment fragment = FeedSettingsFragment.newInstance(feed); @@ -495,7 +520,6 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem viewBinding.header.butFilter.setOnClickListener(v -> FeedItemFilterDialog.newInstance(feed).show(getChildFragmentManager(), null)); viewBinding.header.txtvFailure.setOnClickListener(v -> showErrorDetails()); - headerCreated = true; } private void showErrorDetails() { @@ -516,9 +540,18 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem } private void showFeedInfo() { - if (feed != null) { - FeedInfoFragment fragment = FeedInfoFragment.newInstance(feed); + if (feed == null) { + return; + } + FeedInfoFragment fragment = FeedInfoFragment.newInstance(feed); + if (getActivity() instanceof MainActivity) { ((MainActivity) getActivity()).loadChildFragment(fragment, TransitionEffect.SLIDE); + } else { + getActivity().getSupportFragmentManager() + .beginTransaction() + .replace(R.id.fragmentContainer, fragment, "Info") + .addToBackStack("Info") + .commitAllowingStateLoss(); } } @@ -625,7 +658,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem } private class FeedItemListAdapter extends EpisodeItemListAdapter { - public FeedItemListAdapter(MainActivity mainActivity) { + public FeedItemListAdapter(FragmentActivity mainActivity) { super(mainActivity); } @@ -648,7 +681,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); - if (!inActionMode()) { + if (!inActionMode() && feed.getState() == Feed.STATE_SUBSCRIBED) { menu.findItem(R.id.multi_select).setVisible(true); } MenuItemUtils.setOnClickListeners(menu, FeedItemlistFragment.this::onContextItemSelected); diff --git a/app/src/main/java/de/danoeh/antennapod/ui/screen/home/sections/DownloadsSection.java b/app/src/main/java/de/danoeh/antennapod/ui/screen/home/sections/DownloadsSection.java index cc3288e2f..06de7803a 100644 --- a/app/src/main/java/de/danoeh/antennapod/ui/screen/home/sections/DownloadsSection.java +++ b/app/src/main/java/de/danoeh/antennapod/ui/screen/home/sections/DownloadsSection.java @@ -39,6 +39,8 @@ import java.util.List; public class DownloadsSection extends HomeSection { public static final String TAG = "DownloadsSection"; private static final int NUM_EPISODES = 2; + private static final FeedItemFilter FILTER_DOWNLOADED = new FeedItemFilter( + FeedItemFilter.DOWNLOADED, FeedItemFilter.INCLUDE_NOT_SUBSCRIBED); private EpisodeItemListAdapter adapter; private List items; private Disposable disposable; @@ -52,7 +54,7 @@ public class DownloadsSection extends HomeSection { viewBinding.recyclerView.setOverScrollMode(RecyclerView.OVER_SCROLL_NEVER); viewBinding.recyclerView.setLayoutManager(new LinearLayoutManager(getContext(), RecyclerView.VERTICAL, false)); viewBinding.recyclerView.setRecycledViewPool(((MainActivity) requireActivity()).getRecycledViewPool()); - adapter = new EpisodeItemListAdapter((MainActivity) requireActivity()) { + adapter = new EpisodeItemListAdapter(requireActivity()) { @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); @@ -124,8 +126,7 @@ public class DownloadsSection extends HomeSection { disposable.dispose(); } SortOrder sortOrder = UserPreferences.getDownloadsSortedOrder(); - disposable = Observable.fromCallable(() -> DBReader.getEpisodes(0, NUM_EPISODES, - new FeedItemFilter(FeedItemFilter.DOWNLOADED), sortOrder)) + disposable = Observable.fromCallable(() -> DBReader.getEpisodes(0, NUM_EPISODES, FILTER_DOWNLOADED, sortOrder)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(downloads -> { diff --git a/app/src/main/java/de/danoeh/antennapod/ui/screen/onlinefeedview/FeedItemlistDescriptionAdapter.java b/app/src/main/java/de/danoeh/antennapod/ui/screen/onlinefeedview/FeedItemlistDescriptionAdapter.java deleted file mode 100644 index f46d786cf..000000000 --- a/app/src/main/java/de/danoeh/antennapod/ui/screen/onlinefeedview/FeedItemlistDescriptionAdapter.java +++ /dev/null @@ -1,112 +0,0 @@ -package de.danoeh.antennapod.ui.screen.onlinefeedview; - -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import de.danoeh.antennapod.R; -import de.danoeh.antennapod.model.playback.MediaType; -import de.danoeh.antennapod.net.common.NetworkUtils; -import de.danoeh.antennapod.model.playback.RemoteMedia; -import de.danoeh.antennapod.model.feed.FeedItem; -import de.danoeh.antennapod.playback.service.PlaybackService; -import de.danoeh.antennapod.playback.service.PlaybackServiceStarter; -import de.danoeh.antennapod.ui.common.DateFormatter; -import de.danoeh.antennapod.model.playback.Playable; -import de.danoeh.antennapod.ui.cleaner.HtmlToPlainText; -import de.danoeh.antennapod.ui.StreamingConfirmationDialog; - -import java.util.List; - -/** - * List adapter for showing a list of FeedItems with their title and description. - */ -public class FeedItemlistDescriptionAdapter extends ArrayAdapter { - private static final int MAX_LINES_COLLAPSED = 2; - - public FeedItemlistDescriptionAdapter(Context context, int resource, List objects) { - super(context, resource, objects); - } - - @NonNull - @Override - public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { - Holder holder; - - FeedItem item = getItem(position); - - // Inflate layout - if (convertView == null) { - holder = new Holder(); - LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); - convertView = inflater.inflate(R.layout.itemdescription_listitem, parent, false); - holder.title = convertView.findViewById(R.id.txtvTitle); - holder.pubDate = convertView.findViewById(R.id.txtvPubDate); - holder.description = convertView.findViewById(R.id.txtvDescription); - holder.preview = convertView.findViewById(R.id.butPreview); - - convertView.setTag(holder); - } else { - holder = (Holder) convertView.getTag(); - } - - holder.title.setText(item.getTitle()); - holder.pubDate.setText(DateFormatter.formatAbbrev(getContext(), item.getPubDate())); - if (item.getDescription() != null) { - String description = HtmlToPlainText.getPlainText(item.getDescription()) - .replaceAll("\n", " ") - .replaceAll("\\s+", " ") - .trim(); - holder.description.setText(description); - holder.description.setMaxLines(MAX_LINES_COLLAPSED); - } - holder.description.setTag(Boolean.FALSE); // not expanded - holder.preview.setVisibility(View.GONE); - holder.preview.setOnClickListener(v -> { - if (item.getMedia() == null) { - return; - } - Playable playable = new RemoteMedia(item); - if (!NetworkUtils.isStreamingAllowed()) { - new StreamingConfirmationDialog(getContext(), playable).show(); - return; - } - - new PlaybackServiceStarter(getContext(), playable) - .callEvenIfRunning(true) - .start(); - - if (playable.getMediaType() == MediaType.VIDEO) { - getContext().startActivity(PlaybackService.getPlayerActivityIntent(getContext(), playable)); - } - }); - convertView.setOnClickListener(v -> { - if (holder.description.getTag() == Boolean.TRUE) { - holder.description.setMaxLines(MAX_LINES_COLLAPSED); - holder.preview.setVisibility(View.GONE); - holder.description.setTag(Boolean.FALSE); - } else { - holder.description.setMaxLines(30); - holder.description.setTag(Boolean.TRUE); - - holder.preview.setVisibility(item.getMedia() != null ? View.VISIBLE : View.GONE); - holder.preview.setText(R.string.preview_episode); - } - }); - return convertView; - } - - static class Holder { - TextView title; - TextView pubDate; - TextView description; - Button preview; - } -} diff --git a/app/src/main/java/de/danoeh/antennapod/ui/screen/onlinefeedview/OnlineFeedViewActivity.java b/app/src/main/java/de/danoeh/antennapod/ui/screen/onlinefeedview/OnlineFeedViewActivity.java index 7b8558c3d..be601027c 100644 --- a/app/src/main/java/de/danoeh/antennapod/ui/screen/onlinefeedview/OnlineFeedViewActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/ui/screen/onlinefeedview/OnlineFeedViewActivity.java @@ -4,8 +4,6 @@ import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.content.SharedPreferences; -import android.graphics.LightingColorFilter; import android.os.Bundle; import android.text.Spannable; import android.text.SpannableString; @@ -13,68 +11,45 @@ import android.text.TextUtils; import android.text.style.ForegroundColorSpan; import android.util.Log; import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.UiThread; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; import androidx.appcompat.app.AppCompatActivity; -import com.bumptech.glide.Glide; -import com.bumptech.glide.request.RequestOptions; -import com.google.android.material.snackbar.Snackbar; - +import com.google.android.material.dialog.MaterialAlertDialogBuilder; import de.danoeh.antennapod.R; -import de.danoeh.antennapod.net.download.service.feed.remote.Downloader; -import de.danoeh.antennapod.net.download.service.feed.remote.HttpDownloader; -import de.danoeh.antennapod.ui.appstartintent.MainActivityStarter; -import de.danoeh.antennapod.ui.common.ThemeSwitcher; -import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequestCreator; -import de.danoeh.antennapod.net.discovery.FeedUrlNotFoundException; -import de.danoeh.antennapod.storage.database.FeedDatabaseWriter; -import de.danoeh.antennapod.playback.service.PlaybackServiceInterface; -import de.danoeh.antennapod.ui.screen.download.DownloadErrorLabel; import de.danoeh.antennapod.databinding.EditTextDialogBinding; -import de.danoeh.antennapod.databinding.OnlinefeedviewHeaderBinding; -import de.danoeh.antennapod.event.EpisodeDownloadEvent; -import de.danoeh.antennapod.event.FeedListUpdateEvent; -import de.danoeh.antennapod.event.PlayerStatusEvent; -import de.danoeh.antennapod.storage.preferences.PlaybackPreferences; -import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface; -import de.danoeh.antennapod.storage.preferences.UserPreferences; +import de.danoeh.antennapod.databinding.OnlinefeedviewActivityBinding; +import de.danoeh.antennapod.model.download.DownloadError; import de.danoeh.antennapod.model.download.DownloadRequest; import de.danoeh.antennapod.model.download.DownloadResult; -import de.danoeh.antennapod.storage.database.DBReader; -import de.danoeh.antennapod.storage.database.DBWriter; +import de.danoeh.antennapod.model.feed.Feed; +import de.danoeh.antennapod.net.common.UrlChecker; import de.danoeh.antennapod.net.discovery.CombinedSearcher; +import de.danoeh.antennapod.net.discovery.FeedUrlNotFoundException; import de.danoeh.antennapod.net.discovery.PodcastSearchResult; import de.danoeh.antennapod.net.discovery.PodcastSearcherRegistry; +import de.danoeh.antennapod.net.download.service.feed.remote.Downloader; +import de.danoeh.antennapod.net.download.service.feed.remote.HttpDownloader; +import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequestCreator; import de.danoeh.antennapod.parser.feed.FeedHandler; import de.danoeh.antennapod.parser.feed.FeedHandlerResult; -import de.danoeh.antennapod.model.download.DownloadError; -import de.danoeh.antennapod.ui.common.IntentUtils; -import de.danoeh.antennapod.net.common.UrlChecker; -import de.danoeh.antennapod.ui.cleaner.HtmlToPlainText; -import de.danoeh.antennapod.databinding.OnlinefeedviewActivityBinding; -import de.danoeh.antennapod.model.feed.Feed; -import de.danoeh.antennapod.model.feed.FeedPreferences; -import de.danoeh.antennapod.model.playback.RemoteMedia; import de.danoeh.antennapod.parser.feed.UnsupportedFeedtypeException; +import de.danoeh.antennapod.storage.database.DBReader; +import de.danoeh.antennapod.storage.database.DBWriter; +import de.danoeh.antennapod.storage.database.FeedDatabaseWriter; +import de.danoeh.antennapod.ui.appstartintent.MainActivityStarter; +import de.danoeh.antennapod.ui.common.ThemeSwitcher; import de.danoeh.antennapod.ui.common.ThemeUtils; -import de.danoeh.antennapod.ui.glide.FastBlurTransformation; import de.danoeh.antennapod.ui.preferences.screen.synchronization.AuthenticationDialog; +import de.danoeh.antennapod.ui.screen.download.DownloadErrorLabel; +import de.danoeh.antennapod.ui.screen.feed.FeedItemlistFragment; import io.reactivex.Maybe; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; -import io.reactivex.observers.DisposableMaybeObserver; import io.reactivex.schedulers.Schedulers; -import org.apache.commons.lang3.StringUtils; -import org.greenrobot.eventbus.EventBus; -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; import java.io.File; import java.io.IOException; @@ -95,30 +70,16 @@ import static de.danoeh.antennapod.ui.appstartintent.OnlineFeedviewActivityStart * and the activity will finish as soon as the error dialog is closed. */ public class OnlineFeedViewActivity extends AppCompatActivity { - - private static final int RESULT_ERROR = 2; private static final String TAG = "OnlineFeedViewActivity"; - private static final String PREFS = "OnlineFeedViewActivityPreferences"; - private static final String PREF_LAST_AUTO_DOWNLOAD = "lastAutoDownload"; - private static final int DESCRIPTION_MAX_LINES_COLLAPSED = 4; - - private volatile List feeds; private String selectedDownloadUrl; private Downloader downloader; private String username = null; private String password = null; - private boolean isPaused; - private boolean didPressSubscribe = false; private boolean isFeedFoundBySearch = false; - private Dialog dialog; - private Disposable download; private Disposable parser; - private Disposable updater; - - private OnlinefeedviewHeaderBinding headerBinding; private OnlinefeedviewActivityBinding viewBinding; @Override @@ -128,12 +89,9 @@ public class OnlineFeedViewActivity extends AppCompatActivity { viewBinding = OnlinefeedviewActivityBinding.inflate(getLayoutInflater()); setContentView(viewBinding.getRoot()); - viewBinding.transparentBackground.setOnClickListener(v -> finish()); - viewBinding.closeButton.setOnClickListener(view -> finish()); viewBinding.card.setOnClickListener(null); viewBinding.card.setCardBackgroundColor(ThemeUtils.getColorFromAttr(this, R.attr.colorSurface)); - headerBinding = OnlinefeedviewHeaderBinding.inflate(getLayoutInflater()); String feedUrl = null; if (getIntent().hasExtra(ARG_FEEDURL)) { @@ -149,7 +107,6 @@ public class OnlineFeedViewActivity extends AppCompatActivity { showNoPodcastFoundError(); } else { Log.d(TAG, "Activity was started with url " + feedUrl); - setLoadingLayout(); // Remove subscribeonandroid.com from feed URL in order to subscribe to the actual feed URL if (feedUrl.contains("subscribeonandroid.com")) { feedUrl = feedUrl.replaceFirst("((www.)?(subscribeonandroid.com/))", ""); @@ -167,33 +124,20 @@ public class OnlineFeedViewActivity extends AppCompatActivity { .setNeutralButton(android.R.string.ok, (dialog, which) -> finish()) .setTitle(R.string.error_label) .setMessage(R.string.null_value_podcast_error) - .setOnDismissListener(dialog1 -> { - setResult(RESULT_ERROR); - finish(); - }) + .setOnDismissListener(dialog1 -> finish()) .show()); } - /** - * Displays a progress indicator. - */ - private void setLoadingLayout() { - viewBinding.progressBar.setVisibility(View.VISIBLE); - viewBinding.feedDisplayContainer.setVisibility(View.GONE); - } - @Override protected void onStart() { super.onStart(); isPaused = false; - EventBus.getDefault().register(this); } @Override protected void onStop() { super.onStop(); isPaused = true; - EventBus.getDefault().unregister(this); if (downloader != null && !downloader.isFinished()) { downloader.cancel(); } @@ -205,9 +149,6 @@ public class OnlineFeedViewActivity extends AppCompatActivity { @Override public void onDestroy() { super.onDestroy(); - if(updater != null) { - updater.dispose(); - } if(download != null) { download.dispose(); } @@ -239,7 +180,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity { download = PodcastSearcherRegistry.lookupUrl(url) .subscribeOn(Schedulers.io()) .observeOn(Schedulers.io()) - .subscribe(this::startFeedDownload, + .subscribe(this::downloadIfNotAlreadySubscribed, error -> { if (error instanceof FeedUrlNotFoundException) { tryToRetrieveFeedUrlBySearch((FeedUrlNotFoundException) error); @@ -256,7 +197,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity { if (url != null) { Log.d(TAG, "Successfully retrieve feed url"); isFeedFoundBySearch = true; - startFeedDownload(url); + downloadIfNotAlreadySubscribed(url); } else { showNoPodcastFoundError(); Log.d(TAG, "Failed to retrieve feed url"); @@ -277,6 +218,28 @@ public class OnlineFeedViewActivity extends AppCompatActivity { return null; } + private Feed downloadIfNotAlreadySubscribed(String url) { + download = Maybe.fromCallable(() -> { + List feeds = DBReader.getFeedList(); + for (Feed f : feeds) { + if (f.getDownloadUrl().equals(url)) { + return f; + } + } + return null; + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(subscribedFeed -> { + if (subscribedFeed.getState() == Feed.STATE_SUBSCRIBED) { + openFeed(subscribedFeed.getId()); + } else { + showFeedFragment(subscribedFeed.getId()); + } + }, error -> Log.e(TAG, Log.getStackTraceString(error)), () -> startFeedDownload(url)); + return null; + } + private void startFeedDownload(String url) { Log.d(TAG, "Starting feed download"); selectedDownloadUrl = UrlChecker.prepareUrl(url); @@ -286,7 +249,6 @@ public class OnlineFeedViewActivity extends AppCompatActivity { .build(); download = Observable.fromCallable(() -> { - feeds = DBReader.getFeedList(); downloader = new HttpDownloader(request); downloader.call(); return downloader.getResult(); @@ -315,46 +277,25 @@ public class OnlineFeedViewActivity extends AppCompatActivity { } } - @Subscribe - public void onFeedListChanged(FeedListUpdateEvent event) { - updater = Observable.fromCallable(DBReader::getFeedList) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - feeds -> { - OnlineFeedViewActivity.this.feeds = feeds; - handleUpdatedFeedStatus(); - }, error -> Log.e(TAG, Log.getStackTraceString(error)) - ); - } - - @Subscribe(sticky = true, threadMode = ThreadMode.MAIN) - public void onEventMainThread(EpisodeDownloadEvent event) { - handleUpdatedFeedStatus(); - } - private void parseFeed(String destination) { Log.d(TAG, "Parsing feed"); - parser = Maybe.fromCallable(() -> doParseFeed(destination)) - .subscribeOn(Schedulers.computation()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeWith(new DisposableMaybeObserver() { - @Override - public void onSuccess(@NonNull FeedHandlerResult result) { - showFeedInformation(result.feed, result.alternateFeedUrls); - } - - @Override - public void onComplete() { - // Ignore null result: We showed the discovery dialog. - } - - @Override - public void onError(@NonNull Throwable error) { - showErrorDialog(error.getMessage(), ""); - Log.d(TAG, "Feed parser exception: " + Log.getStackTraceString(error)); - } - }); + parser = Observable.fromCallable(() -> { + FeedHandlerResult handlerResult = doParseFeed(destination); + Feed feed = handlerResult.feed; + feed.setState(Feed.STATE_NOT_SUBSCRIBED); + feed.setLastRefreshAttempt(System.currentTimeMillis()); + FeedDatabaseWriter.updateFeed(this, feed, false); + Feed feedFromDb = DBReader.getFeed(feed.getId(), false, 0, Integer.MAX_VALUE); + feedFromDb.getPreferences().setKeepUpdated(false); + DBWriter.setFeedPreferences(feedFromDb.getPreferences()); + return feed.getId(); + }) + .subscribeOn(Schedulers.computation()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(this::showFeedFragment, error -> { + error.printStackTrace(); + showErrorDialog(error.getMessage(), ""); + }); } /** @@ -392,123 +333,23 @@ public class OnlineFeedViewActivity extends AppCompatActivity { } } - /** - * Called when feed parsed successfully. - * This method is executed on the GUI thread. - */ - private void showFeedInformation(final Feed feed, Map alternateFeedUrls) { - viewBinding.progressBar.setVisibility(View.GONE); - viewBinding.feedDisplayContainer.setVisibility(View.VISIBLE); + private void showFeedFragment(long id) { if (isFeedFoundBySearch) { - int resId = R.string.no_feed_url_podcast_found_by_search; - Snackbar.make(findViewById(android.R.id.content), resId, Snackbar.LENGTH_LONG).show(); + Toast.makeText(this, R.string.no_feed_url_podcast_found_by_search, Toast.LENGTH_LONG).show(); } - viewBinding.backgroundImage.setColorFilter(new LightingColorFilter(0xff828282, 0x000000)); - - viewBinding.listView.addHeaderView(headerBinding.getRoot()); - viewBinding.listView.setSelector(android.R.color.transparent); - viewBinding.listView.setAdapter(new FeedItemlistDescriptionAdapter(this, 0, feed.getItems())); - - if (StringUtils.isNotBlank(feed.getImageUrl())) { - Glide.with(this) - .load(feed.getImageUrl()) - .apply(new RequestOptions() - .placeholder(R.color.light_gray) - .error(R.color.light_gray) - .fitCenter() - .dontAnimate()) - .into(viewBinding.coverImage); - Glide.with(this) - .load(feed.getImageUrl()) - .apply(new RequestOptions() - .placeholder(R.color.image_readability_tint) - .error(R.color.image_readability_tint) - .transform(new FastBlurTransformation()) - .dontAnimate()) - .into(viewBinding.backgroundImage); - } - - viewBinding.titleLabel.setText(feed.getTitle()); - viewBinding.authorLabel.setText(feed.getAuthor()); - headerBinding.txtvDescription.setText(HtmlToPlainText.getPlainText(feed.getDescription())); - - viewBinding.subscribeButton.setOnClickListener(v -> { - if (feedInFeedlist()) { - openFeed(); - } else { - FeedDatabaseWriter.updateFeed(this, feed, false); - didPressSubscribe = true; - handleUpdatedFeedStatus(); - } - }); - - viewBinding.stopPreviewButton.setOnClickListener(v -> { - PlaybackPreferences.writeNoMediaPlaying(); - IntentUtils.sendLocalBroadcast(this, PlaybackServiceInterface.ACTION_SHUTDOWN_PLAYBACK_SERVICE); - }); - - if (UserPreferences.isEnableAutodownload()) { - SharedPreferences preferences = getSharedPreferences(PREFS, MODE_PRIVATE); - viewBinding.autoDownloadCheckBox.setChecked(preferences.getBoolean(PREF_LAST_AUTO_DOWNLOAD, true)); - } - - headerBinding.txtvDescription.setMaxLines(DESCRIPTION_MAX_LINES_COLLAPSED); - headerBinding.txtvDescription.setOnClickListener(v -> { - if (headerBinding.txtvDescription.getMaxLines() > DESCRIPTION_MAX_LINES_COLLAPSED) { - headerBinding.txtvDescription.setMaxLines(DESCRIPTION_MAX_LINES_COLLAPSED); - } else { - headerBinding.txtvDescription.setMaxLines(2000); - } - }); - - if (alternateFeedUrls.isEmpty()) { - viewBinding.alternateUrlsSpinner.setVisibility(View.GONE); - } else { - viewBinding.alternateUrlsSpinner.setVisibility(View.VISIBLE); - - final List alternateUrlsList = new ArrayList<>(); - final List alternateUrlsTitleList = new ArrayList<>(); - - alternateUrlsList.add(feed.getDownloadUrl()); - alternateUrlsTitleList.add(feed.getTitle()); - - - alternateUrlsList.addAll(alternateFeedUrls.keySet()); - for (String url : alternateFeedUrls.keySet()) { - alternateUrlsTitleList.add(alternateFeedUrls.get(url)); - } - - ArrayAdapter adapter = new ArrayAdapter(this, - R.layout.alternate_urls_item, alternateUrlsTitleList) { - @Override - public View getDropDownView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { - // reusing the old view causes a visual bug on Android <= 10 - return super.getDropDownView(position, null, parent); - } - }; - - adapter.setDropDownViewResource(R.layout.alternate_urls_dropdown_item); - viewBinding.alternateUrlsSpinner.setAdapter(adapter); - viewBinding.alternateUrlsSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - selectedDownloadUrl = alternateUrlsList.get(position); - } - - @Override - public void onNothingSelected(AdapterView parent) { - - } - }); - } - handleUpdatedFeedStatus(); + viewBinding.progressBar.setVisibility(View.GONE); + FeedItemlistFragment fragment = FeedItemlistFragment.newInstance(id); + getSupportFragmentManager() + .beginTransaction() + .replace(R.id.fragmentContainer, fragment, FeedItemlistFragment.TAG) + .commitAllowingStateLoss(); } - private void openFeed() { + private void openFeed(long feedId) { // feed.getId() is always 0, we have to retrieve the id from the feed list from the database MainActivityStarter mainActivityStarter = new MainActivityStarter(this); - mainActivityStarter.withOpenFeed(getFeedId()); + mainActivityStarter.withOpenFeed(feedId); if (getIntent().getBooleanExtra(ARG_STARTED_FROM_SEARCH, false)) { mainActivityStarter.withAddToBackStack(); } @@ -516,60 +357,6 @@ public class OnlineFeedViewActivity extends AppCompatActivity { startActivity(mainActivityStarter.getIntent()); } - private void handleUpdatedFeedStatus() { - if (DownloadServiceInterface.get().isDownloadingEpisode(selectedDownloadUrl)) { - viewBinding.subscribeButton.setEnabled(false); - viewBinding.subscribeButton.setText(R.string.subscribing_label); - } else if (feedInFeedlist()) { - viewBinding.subscribeButton.setEnabled(true); - viewBinding.subscribeButton.setText(R.string.open_podcast); - if (didPressSubscribe) { - didPressSubscribe = false; - - Feed feed1 = DBReader.getFeed(getFeedId(), false, 0, 0); - FeedPreferences feedPreferences = feed1.getPreferences(); - if (UserPreferences.isEnableAutodownload()) { - boolean autoDownload = viewBinding.autoDownloadCheckBox.isChecked(); - feedPreferences.setAutoDownload(autoDownload); - - SharedPreferences preferences = getSharedPreferences(PREFS, MODE_PRIVATE); - SharedPreferences.Editor editor = preferences.edit(); - editor.putBoolean(PREF_LAST_AUTO_DOWNLOAD, autoDownload); - editor.apply(); - } - if (username != null) { - feedPreferences.setUsername(username); - feedPreferences.setPassword(password); - } - DBWriter.setFeedPreferences(feedPreferences); - - openFeed(); - } - } else { - viewBinding.subscribeButton.setEnabled(true); - viewBinding.subscribeButton.setText(R.string.subscribe_label); - if (UserPreferences.isEnableAutodownload()) { - viewBinding.autoDownloadCheckBox.setVisibility(View.VISIBLE); - } - } - } - - private boolean feedInFeedlist() { - return getFeedId() != 0; - } - - private long getFeedId() { - if (feeds == null) { - return 0; - } - for (Feed f : feeds) { - if (f.getDownloadUrl().equals(selectedDownloadUrl)) { - return f.getId(); - } - } - return 0; - } - @UiThread private void showErrorDialog(String errorMsg, String details) { if (!isFinishing() && !isPaused) { @@ -589,7 +376,6 @@ public class OnlineFeedViewActivity extends AppCompatActivity { builder.setNeutralButton(R.string.edit_url_menu, (dialog, which) -> editUrl()); } builder.setOnCancelListener(dialog -> { - setResult(RESULT_ERROR); finish(); }); if (dialog != null && dialog.isShowing()) { @@ -608,24 +394,15 @@ public class OnlineFeedViewActivity extends AppCompatActivity { } builder.setView(dialogBinding.getRoot()); builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> { - setLoadingLayout(); lookupUrlAndDownload(dialogBinding.urlEditText.getText().toString()); }); builder.setNegativeButton(R.string.cancel_label, (dialog1, which) -> dialog1.cancel()); builder.setOnCancelListener(dialog1 -> { - setResult(RESULT_ERROR); finish(); }); builder.show(); } - @Subscribe(threadMode = ThreadMode.MAIN) - public void playbackStateChanged(PlayerStatusEvent event) { - boolean isPlayingPreview = - PlaybackPreferences.getCurrentlyPlayingMediaType() == RemoteMedia.PLAYABLE_TYPE_REMOTE_MEDIA; - viewBinding.stopPreviewButton.setVisibility(isPlayingPreview ? View.VISIBLE : View.GONE); - } - /** * * @return true if a FeedDiscoveryDialog is shown, false otherwise (e.g., due to no feed found). @@ -657,7 +434,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity { if (urls.size() == 1) { // Skip dialog and display the item directly resetIntent(urls.get(0)); - startFeedDownload(urls.get(0)); + downloadIfNotAlreadySubscribed(urls.get(0)); return true; } @@ -667,7 +444,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity { String selectedUrl = urls.get(which); dialog.dismiss(); resetIntent(selectedUrl); - startFeedDownload(selectedUrl); + downloadIfNotAlreadySubscribed(selectedUrl); }; MaterialAlertDialogBuilder ab = new MaterialAlertDialogBuilder(OnlineFeedViewActivity.this) @@ -704,7 +481,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity { protected void onConfirmed(String username, String password) { OnlineFeedViewActivity.this.username = username; OnlineFeedViewActivity.this.password = password; - startFeedDownload(feedUrl); + downloadIfNotAlreadySubscribed(feedUrl); } } diff --git a/app/src/main/java/de/danoeh/antennapod/ui/screen/playback/audio/AudioPlayerFragment.java b/app/src/main/java/de/danoeh/antennapod/ui/screen/playback/audio/AudioPlayerFragment.java index e66ed6d3e..7aa9da503 100644 --- a/app/src/main/java/de/danoeh/antennapod/ui/screen/playback/audio/AudioPlayerFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/ui/screen/playback/audio/AudioPlayerFragment.java @@ -25,8 +25,10 @@ import com.google.android.material.appbar.MaterialToolbar; import com.google.android.material.bottomsheet.BottomSheetBehavior; import com.google.android.material.elevation.SurfaceColors; +import de.danoeh.antennapod.model.feed.Feed; import de.danoeh.antennapod.playback.service.PlaybackController; import de.danoeh.antennapod.ui.appstartintent.MediaButtonStarter; +import de.danoeh.antennapod.ui.appstartintent.OnlineFeedviewActivityStarter; import de.danoeh.antennapod.ui.chapters.ChapterUtils; import de.danoeh.antennapod.ui.episodes.PlaybackSpeedUtils; import de.danoeh.antennapod.ui.episodes.TimeSpeedConverter; @@ -497,14 +499,25 @@ public class AudioPlayerFragment extends Fragment implements return true; } else if (itemId == R.id.open_feed_item) { if (feedItem != null) { - Intent intent = MainActivity.getIntentToOpenFeed(getContext(), feedItem.getFeedId()); - startActivity(intent); + openFeed(feedItem.getFeed()); } return true; } return false; } + private void openFeed(Feed feed) { + if (feed == null) { + return; + } + if (feed.getState() == Feed.STATE_SUBSCRIBED) { + Intent intent = MainActivity.getIntentToOpenFeed(getContext(), feed.getId()); + startActivity(intent); + } else { + startActivity(new OnlineFeedviewActivityStarter(getContext(), feed.getDownloadUrl()).getIntent()); + } + } + public void fadePlayerToToolbar(float slideOffset) { float playerFadeProgress = Math.max(0.0f, Math.min(0.2f, slideOffset - 0.2f)) / 0.2f; View player = getView().findViewById(R.id.playerFragment); diff --git a/app/src/main/java/de/danoeh/antennapod/ui/screen/playback/audio/CoverFragment.java b/app/src/main/java/de/danoeh/antennapod/ui/screen/playback/audio/CoverFragment.java index 073d5b0be..d4931380e 100644 --- a/app/src/main/java/de/danoeh/antennapod/ui/screen/playback/audio/CoverFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/ui/screen/playback/audio/CoverFragment.java @@ -32,6 +32,8 @@ import com.bumptech.glide.request.RequestOptions; import com.google.android.material.snackbar.Snackbar; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; +import de.danoeh.antennapod.model.feed.Feed; +import de.danoeh.antennapod.ui.appstartintent.OnlineFeedviewActivityStarter; import de.danoeh.antennapod.ui.chapters.ChapterUtils; import de.danoeh.antennapod.ui.screen.chapter.ChaptersFragment; import de.danoeh.antennapod.playback.service.PlaybackController; @@ -122,9 +124,7 @@ public class CoverFragment extends Fragment { + "\u00A0" + StringUtils.replace(StringUtils.stripToEmpty(pubDateStr), " ", "\u00A0")); if (media instanceof FeedMedia) { - Intent openFeed = MainActivity.getIntentToOpenFeed(requireContext(), - ((FeedMedia) media).getItem().getFeedId()); - viewBinding.txtvPodcastTitle.setOnClickListener(v -> startActivity(openFeed)); + viewBinding.txtvPodcastTitle.setOnClickListener(v -> openFeed(((FeedMedia) media).getItem().getFeed())); } else { viewBinding.txtvPodcastTitle.setOnClickListener(null); } @@ -164,6 +164,18 @@ public class CoverFragment extends Fragment { updateChapterControlVisibility(); } + private void openFeed(Feed feed) { + if (feed == null) { + return; + } + if (feed.getState() == Feed.STATE_SUBSCRIBED) { + Intent intent = MainActivity.getIntentToOpenFeed(getContext(), feed.getId()); + startActivity(intent); + } else { + startActivity(new OnlineFeedviewActivityStarter(getContext(), feed.getDownloadUrl()).getIntent()); + } + } + private void updateChapterControlVisibility() { boolean chapterControlVisible = false; if (media.getChapters() != null) { diff --git a/app/src/main/res/layout/alternate_urls_dropdown_item.xml b/app/src/main/res/layout/alternate_urls_dropdown_item.xml deleted file mode 100644 index 3c4dcc7fc..000000000 --- a/app/src/main/res/layout/alternate_urls_dropdown_item.xml +++ /dev/null @@ -1,8 +0,0 @@ - - diff --git a/app/src/main/res/layout/alternate_urls_item.xml b/app/src/main/res/layout/alternate_urls_item.xml deleted file mode 100644 index 54e05b49d..000000000 --- a/app/src/main/res/layout/alternate_urls_item.xml +++ /dev/null @@ -1,9 +0,0 @@ - - diff --git a/app/src/main/res/layout/feedinfo.xml b/app/src/main/res/layout/feedinfo.xml index d1acc1e37..7b7937b9d 100644 --- a/app/src/main/res/layout/feedinfo.xml +++ b/app/src/main/res/layout/feedinfo.xml @@ -65,13 +65,13 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" + android:paddingTop="16dp" android:paddingHorizontal="@dimen/additional_horizontal_spacing"> @@ -96,7 +97,6 @@ android:id="@+id/supportHeadingLabel" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="16dp" android:layout_marginBottom="4dp" android:text="@string/support_funding_label" android:textColor="?android:attr/textColorPrimary" @@ -119,7 +119,6 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="18sp" - android:layout_marginTop="16dp" android:layout_marginBottom="4dp" android:text="@string/description_label" android:textColor="?android:attr/textColorPrimary" @@ -129,7 +128,6 @@ android:id="@+id/descriptionLabel" android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="@string/design_time_lorem_ipsum" android:textIsSelectable="true" tools:background="@android:color/holo_green_dark" /> diff --git a/app/src/main/res/layout/feeditem_fragment.xml b/app/src/main/res/layout/feeditem_fragment.xml index 37db9067c..df95b8c15 100644 --- a/app/src/main/res/layout/feeditem_fragment.xml +++ b/app/src/main/res/layout/feeditem_fragment.xml @@ -174,6 +174,20 @@ + + +