Add episodes without subscribing (#7098)
This commit is contained in:
parent
53ce6cd71a
commit
084723ad76
|
@ -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 {
|
||||
|
|
|
@ -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<EpisodeItemViewHolder>
|
||||
implements View.OnCreateContextMenuListener {
|
||||
|
||||
private final WeakReference<MainActivity> mainActivityRef;
|
||||
private final WeakReference<FragmentActivity> mainActivityRef;
|
||||
private List<FeedItem> 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<EpisodeItemViewHol
|
|||
holder.bind(item);
|
||||
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
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());
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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<String> 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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -285,7 +285,7 @@ public class CompletedDownloadsFragment extends Fragment
|
|||
disposable = Observable.fromCallable(() -> {
|
||||
SortOrder sortOrder = UserPreferences.getDownloadsSortedOrder();
|
||||
List<FeedItem> downloadedItems = DBReader.getEpisodes(0, Integer.MAX_VALUE,
|
||||
new FeedItemFilter(FeedItemFilter.DOWNLOADED), sortOrder);
|
||||
new FeedItemFilter(FeedItemFilter.DOWNLOADED, FeedItemFilter.INCLUDE_NOT_SUBSCRIBED), sortOrder);
|
||||
|
||||
List<String> mediaUrls = new ArrayList<>();
|
||||
if (runningDownloads == null) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<FeedItem> 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 -> {
|
||||
|
|
|
@ -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<FeedItem> {
|
||||
private static final int MAX_LINES_COLLAPSED = 2;
|
||||
|
||||
public FeedItemlistDescriptionAdapter(Context context, int resource, List<FeedItem> 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;
|
||||
}
|
||||
}
|
|
@ -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<Feed> 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<Feed> 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<FeedHandlerResult>() {
|
||||
@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<String, String> 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<String> alternateUrlsList = new ArrayList<>();
|
||||
final List<String> 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<String> adapter = new ArrayAdapter<String>(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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<CheckedTextView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@android:id/text1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingVertical="8dp"
|
||||
style="?android:attr/spinnerDropDownItemStyle" />
|
|
@ -1,9 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@android:id/text1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="3"
|
||||
android:textAlignment="inherit"
|
||||
style="?android:attr/spinnerItemStyle" />
|
|
@ -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">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/statisticsHeadingLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:textSize="18sp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="@string/statistics_label"
|
||||
|
@ -89,6 +89,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:minWidth="0dp"
|
||||
android:minHeight="0dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:text="@string/statistics_view_all"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton" />
|
||||
|
||||
|
@ -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" />
|
||||
|
||||
|
|
|
@ -174,6 +174,20 @@
|
|||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/nonSubscribedWarningLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="8dp"
|
||||
android:background="@drawable/bg_message_info"
|
||||
android:textColor="?attr/colorAccent"
|
||||
android:visibility="gone"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
android:text="@string/state_deleted_not_subscribed" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/noMediaLabel"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -28,6 +28,14 @@
|
|||
android:layout_width="148dp"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/butSubscribe"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/subscribe_label"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/butShowInfo"
|
||||
android:layout_width="40dp"
|
||||
|
@ -187,4 +195,52 @@
|
|||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/descriptionContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:background="?android:attr/colorBackground"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="8dp"
|
||||
android:background="@drawable/bg_message_info"
|
||||
android:textColor="?attr/colorAccent"
|
||||
android:text="@string/state_deleted_not_subscribed" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="@string/description_label"
|
||||
style="@style/TextAppearance.Material3.TitleMedium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/headerDescriptionLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:lineHeight="20dp"
|
||||
android:maxLines="3"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
style="@style/AntennaPod.TextView.ListItemBody"
|
||||
tools:text="@string/design_time_lorem_ipsum" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/preview_episodes"
|
||||
android:layout_marginBottom="8dp"
|
||||
style="@style/TextAppearance.Material3.TitleMedium" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="20dp"
|
||||
android:paddingVertical="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtvPubDate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
style="@android:style/TextAppearance.Small"
|
||||
tools:text="22 Jan 2016"
|
||||
tools:background="@android:color/holo_green_dark" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtvTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="12dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
|
||||
tools:text="Feed item title"
|
||||
tools:background="@android:color/holo_green_dark" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtvDescription"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
style="@style/AntennaPod.TextView.ListItemBody"
|
||||
tools:text="Feed item description"
|
||||
tools:background="@android:color/holo_green_dark" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/butPreview"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/preview_episode"
|
||||
android:layout_gravity="end|right"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton" />
|
||||
|
||||
</LinearLayout>
|
|
@ -2,7 +2,6 @@
|
|||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/transparentBackground"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
@ -11,169 +10,22 @@
|
|||
android:id="@+id/card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="24dp"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginVertical="56dp"
|
||||
android:elevation="16dp"
|
||||
app:cardCornerRadius="8dp">
|
||||
|
||||
<FrameLayout
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/fragmentContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
style="?android:attr/progressBarStyle" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/feed_display_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/feeditemlist_header_height"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:background="@color/feed_image_bg">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/backgroundImage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/coverImage"
|
||||
android:layout_width="@dimen/thumbnail_length_onlinefeedview"
|
||||
android:layout_height="@dimen/thumbnail_length_onlinefeedview"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:background="@drawable/bg_rounded_corners"
|
||||
android:clipToOutline="true"
|
||||
android:importantForAccessibility="no"
|
||||
tools:src="@tools:sample/avatars" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/titleLabel"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginRight="24dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_toRightOf="@id/coverImage"
|
||||
android:layout_toEndOf="@id/coverImage"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:shadowColor="@color/black"
|
||||
android:shadowRadius="3"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="20sp"
|
||||
android:textFontWeight="800"
|
||||
tools:text="Podcast title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/author_label"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/titleLabel"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_toRightOf="@id/coverImage"
|
||||
android:layout_toEndOf="@id/coverImage"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:shadowColor="@color/black"
|
||||
android:shadowRadius="3"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/text_size_small"
|
||||
tools:text="Podcast author" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/closeButton"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginRight="12dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:src="@drawable/ic_close_white" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/alternate_urls_spinner"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:dropDownWidth="match_parent"
|
||||
android:padding="8dp"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="@dimen/text_size_micro" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/subscribeButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="@string/subscribe_label" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/autoDownloadCheckBox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="left"
|
||||
android:checked="true"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/auto_download_label"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/stopPreviewButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="@string/stop_preview"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ListView
|
||||
android:id="@+id/listView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingTop="16dp"
|
||||
android:clipToPadding="false" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
style="?android:attr/progressBarStyle" />
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="20dp"
|
||||
android:paddingRight="20dp"
|
||||
android:layout_marginBottom="8dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="@string/description_label"
|
||||
style="@style/TextAppearance.Material3.TitleMedium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtvDescription"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:ellipsize="end"
|
||||
android:lineHeight="20dp"
|
||||
style="@style/AntennaPod.TextView.ListItemBody"
|
||||
tools:text="@string/design_time_lorem_ipsum" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:text="@string/episodes_label"
|
||||
style="@style/TextAppearance.Material3.TitleMedium" />
|
||||
|
||||
</LinearLayout>
|
|
@ -15,6 +15,8 @@ import org.apache.commons.lang3.StringUtils;
|
|||
public class Feed {
|
||||
|
||||
public static final int FEEDFILETYPE_FEED = 0;
|
||||
public static final int STATE_SUBSCRIBED = 0;
|
||||
public static final int STATE_NOT_SUBSCRIBED = 1;
|
||||
public static final String TYPE_RSS2 = "rss";
|
||||
public static final String TYPE_ATOM1 = "atom";
|
||||
public static final String PREFIX_LOCAL_FOLDER = "antennapod_local:";
|
||||
|
@ -101,6 +103,7 @@ public class Feed {
|
|||
*/
|
||||
@Nullable
|
||||
private SortOrder sortOrder;
|
||||
private int state;
|
||||
|
||||
/**
|
||||
* This constructor is used for restoring a feed from the database.
|
||||
|
@ -109,7 +112,7 @@ public class Feed {
|
|||
String description, String paymentLinks, String author, String language,
|
||||
String type, String feedIdentifier, String imageUrl, String fileUrl,
|
||||
String downloadUrl, long lastRefreshAttempt, boolean paged, String nextPageLink,
|
||||
String filter, @Nullable SortOrder sortOrder, boolean lastUpdateFailed) {
|
||||
String filter, @Nullable SortOrder sortOrder, boolean lastUpdateFailed, int state) {
|
||||
this.localFileUrl = fileUrl;
|
||||
this.downloadUrl = downloadUrl;
|
||||
this.lastRefreshAttempt = lastRefreshAttempt;
|
||||
|
@ -135,6 +138,7 @@ public class Feed {
|
|||
}
|
||||
setSortOrder(sortOrder);
|
||||
this.lastUpdateFailed = lastUpdateFailed;
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -144,7 +148,7 @@ public class Feed {
|
|||
String author, String language, String type, String feedIdentifier, String imageUrl, String fileUrl,
|
||||
String downloadUrl, long lastRefreshAttempt) {
|
||||
this(id, lastModified, title, null, link, description, paymentLink, author, language, type, feedIdentifier,
|
||||
imageUrl, fileUrl, downloadUrl, lastRefreshAttempt, false, null, null, null, false);
|
||||
imageUrl, fileUrl, downloadUrl, lastRefreshAttempt, false, null, null, null, false, STATE_SUBSCRIBED);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -468,4 +472,12 @@ public class Feed {
|
|||
public boolean isLocalFeed() {
|
||||
return downloadUrl.startsWith(PREFIX_LOCAL_FOLDER);
|
||||
}
|
||||
|
||||
public int getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(int state) {
|
||||
this.state = state;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ public class FeedItemFilter implements Serializable {
|
|||
public final boolean showIsFavorite;
|
||||
public final boolean showNotFavorite;
|
||||
public final boolean showInHistory;
|
||||
public final boolean includeNotSubscribed;
|
||||
|
||||
public static final String PLAYED = "played";
|
||||
public static final String UNPLAYED = "unplayed";
|
||||
|
@ -39,6 +40,7 @@ public class FeedItemFilter implements Serializable {
|
|||
public static final String DOWNLOADED = "downloaded";
|
||||
public static final String NOT_DOWNLOADED = "not_downloaded";
|
||||
public static final String IS_IN_HISTORY = "is_in_history";
|
||||
public static final String INCLUDE_NOT_SUBSCRIBED = "include_not_subscribed";
|
||||
|
||||
public static FeedItemFilter unfiltered() {
|
||||
return new FeedItemFilter("");
|
||||
|
@ -48,6 +50,10 @@ public class FeedItemFilter implements Serializable {
|
|||
this(TextUtils.split(properties, ","));
|
||||
}
|
||||
|
||||
public FeedItemFilter(FeedItemFilter filter, String... additionalProperties) {
|
||||
this(TextUtils.join(",", filter.getValues()) + "," + TextUtils.join(",", additionalProperties));
|
||||
}
|
||||
|
||||
public FeedItemFilter(String... properties) {
|
||||
this.properties = properties;
|
||||
|
||||
|
@ -66,6 +72,7 @@ public class FeedItemFilter implements Serializable {
|
|||
showNotFavorite = hasProperty(NOT_FAVORITE);
|
||||
showNew = hasProperty(NEW);
|
||||
showInHistory = hasProperty(IS_IN_HISTORY);
|
||||
includeNotSubscribed = hasProperty(INCLUDE_NOT_SUBSCRIBED);
|
||||
}
|
||||
|
||||
private boolean hasProperty(String property) {
|
||||
|
@ -112,6 +119,9 @@ public class FeedItemFilter implements Serializable {
|
|||
} else if (showInHistory && item.getMedia() != null
|
||||
&& item.getMedia().getPlaybackCompletionDate().getTime() == 0) {
|
||||
return false;
|
||||
} else if (!includeNotSubscribed && item.getFeed() != null
|
||||
&& item.getFeed().getState() != Feed.STATE_SUBSCRIBED) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ public class SubscriptionsFilter {
|
|||
private final String[] properties;
|
||||
|
||||
private boolean showIfCounterGreaterZero = false;
|
||||
private boolean hideNonSubscribedFeeds = true;
|
||||
|
||||
private boolean showAutoDownloadEnabled = false;
|
||||
private boolean showAutoDownloadDisabled = false;
|
||||
|
@ -67,10 +68,6 @@ public class SubscriptionsFilter {
|
|||
* Run a list of feed items through the filter.
|
||||
*/
|
||||
public List<Feed> filter(List<Feed> items, Map<Long, Integer> feedCounters) {
|
||||
if (properties.length == 0) {
|
||||
return items;
|
||||
}
|
||||
|
||||
List<Feed> result = new ArrayList<>();
|
||||
|
||||
for (Feed item : items) {
|
||||
|
@ -95,6 +92,10 @@ public class SubscriptionsFilter {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (hideNonSubscribedFeeds && item.getState() != Feed.STATE_SUBSCRIBED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the item reaches here, it meets all criteria (except counter > 0)
|
||||
result.add(item);
|
||||
}
|
||||
|
|
|
@ -58,7 +58,8 @@ public class ItunesTopListLoader {
|
|||
List<PodcastSearchResult> suggestedPodcasts, List<Feed> subscribedFeeds, int limit) {
|
||||
Set<String> subscribedPodcastsSet = new HashSet<>();
|
||||
for (Feed subscribedFeed : subscribedFeeds) {
|
||||
if (subscribedFeed.getTitle() != null && subscribedFeed.getAuthor() != null) {
|
||||
if (subscribedFeed.getTitle() != null && subscribedFeed.getAuthor() != null
|
||||
&& subscribedFeed.getState() == Feed.STATE_SUBSCRIBED) {
|
||||
subscribedPodcastsSet.add(subscribedFeed.getTitle().trim() + " - " + subscribedFeed.getAuthor().trim());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import android.util.Log;
|
|||
import androidx.annotation.NonNull;
|
||||
|
||||
import de.danoeh.antennapod.model.MediaMetadataRetrieverCompat;
|
||||
import de.danoeh.antennapod.model.feed.Feed;
|
||||
import de.danoeh.antennapod.net.sync.serviceinterface.SynchronizationQueueSink;
|
||||
import de.danoeh.antennapod.ui.chapters.ChapterUtils;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
@ -104,7 +105,7 @@ public class MediaDownloadedHandler implements Runnable {
|
|||
FeedMedia.FEEDFILETYPE_FEEDMEDIA, false, DownloadError.ERROR_DB_ACCESS_ERROR, e.getMessage());
|
||||
}
|
||||
|
||||
if (item != null) {
|
||||
if (item != null && item.getFeed().getState() == Feed.STATE_SUBSCRIBED) {
|
||||
EpisodeAction action = new EpisodeAction.Builder(item, EpisodeAction.DOWNLOAD)
|
||||
.currentTimestamp()
|
||||
.build();
|
||||
|
|
|
@ -35,6 +35,7 @@ import de.danoeh.antennapod.model.download.DownloadRequest;
|
|||
|
||||
import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequestBuilder;
|
||||
import de.danoeh.antennapod.parser.feed.FeedHandlerResult;
|
||||
import de.danoeh.antennapod.storage.database.NonSubscribedFeedsCleaner;
|
||||
import de.danoeh.antennapod.ui.notifications.NotificationUtils;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
@ -69,7 +70,7 @@ public class FeedUpdateWorker extends Worker {
|
|||
Iterator<Feed> itr = toUpdate.iterator();
|
||||
while (itr.hasNext()) {
|
||||
Feed feed = itr.next();
|
||||
if (!feed.getPreferences().getKeepUpdated()) {
|
||||
if (!feed.getPreferences().getKeepUpdated() || feed.getState() != Feed.STATE_SUBSCRIBED) {
|
||||
itr.remove();
|
||||
continue;
|
||||
}
|
||||
|
@ -99,8 +100,9 @@ public class FeedUpdateWorker extends Worker {
|
|||
}
|
||||
refreshFeeds(toUpdate, force);
|
||||
|
||||
notificationManager.cancel(R.id.notification_updating_feeds);
|
||||
NonSubscribedFeedsCleaner.deleteOldNonSubscribedFeeds(getApplicationContext());
|
||||
AutoDownloadManager.getInstance().autodownloadUndownloadedItems(getApplicationContext());
|
||||
notificationManager.cancel(R.id.notification_updating_feeds);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package de.danoeh.antennapod.net.sync.serviceinterface;
|
|||
|
||||
import android.content.Context;
|
||||
|
||||
import de.danoeh.antennapod.model.feed.Feed;
|
||||
import de.danoeh.antennapod.storage.preferences.SynchronizationSettings;
|
||||
import de.danoeh.antennapod.model.feed.FeedMedia;
|
||||
|
||||
|
@ -62,7 +63,8 @@ public class SynchronizationQueueSink {
|
|||
if (!SynchronizationSettings.isProviderConnected()) {
|
||||
return;
|
||||
}
|
||||
if (media.getItem() == null || media.getItem().getFeed().isLocalFeed()) {
|
||||
if (media.getItem() == null || media.getItem().getFeed().isLocalFeed()
|
||||
|| media.getItem().getFeed().getState() != Feed.STATE_SUBSCRIBED) {
|
||||
return;
|
||||
}
|
||||
if (media.getStartPosition() < 0 || (!completed && media.getStartPosition() >= media.getPosition())) {
|
||||
|
|
|
@ -436,7 +436,9 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
|||
DBReader.getTotalEpisodeCount(new FeedItemFilter(FeedItemFilter.UNPLAYED))));
|
||||
List<Feed> feeds = DBReader.getFeedList();
|
||||
for (Feed feed : feeds) {
|
||||
mediaItems.add(createBrowsableMediaItemForFeed(feed));
|
||||
if (feed.getState() == Feed.STATE_SUBSCRIBED) {
|
||||
mediaItems.add(createBrowsableMediaItemForFeed(feed));
|
||||
}
|
||||
}
|
||||
return mediaItems;
|
||||
}
|
||||
|
|
|
@ -356,7 +356,9 @@ public final class DBReader {
|
|||
try (FeedCursor cursor = new FeedCursor(adapter.getFeedCursor(feedId))) {
|
||||
if (cursor.moveToNext()) {
|
||||
feed = cursor.getFeed();
|
||||
FeedItemFilter filter = filtered ? feed.getItemFilter() : FeedItemFilter.unfiltered();
|
||||
FeedItemFilter filter = (filtered && feed.getItemFilter() != null)
|
||||
? feed.getItemFilter() : FeedItemFilter.unfiltered();
|
||||
filter = new FeedItemFilter(filter, FeedItemFilter.INCLUDE_NOT_SUBSCRIBED);
|
||||
List<FeedItem> items = getFeedItemList(feed, filter, feed.getSortOrder(), offset, limit);
|
||||
for (FeedItem item : items) {
|
||||
item.setFeed(feed);
|
||||
|
@ -668,9 +670,10 @@ public final class DBReader {
|
|||
final Map<Long, Integer> feedCounters = adapter.getFeedCounters(feedCounter);
|
||||
List<Feed> feeds = getFeedList();
|
||||
|
||||
if (subscriptionsFilter != null) {
|
||||
feeds = subscriptionsFilter.filter(feeds, feedCounters);
|
||||
if (subscriptionsFilter == null) {
|
||||
subscriptionsFilter = new SubscriptionsFilter("");
|
||||
}
|
||||
feeds = subscriptionsFilter.filter(feeds, feedCounters);
|
||||
|
||||
Comparator<Feed> comparator;
|
||||
switch (feedOrder) {
|
||||
|
|
|
@ -6,6 +6,7 @@ import android.database.sqlite.SQLiteDatabase;
|
|||
import android.media.MediaMetadataRetriever;
|
||||
import android.util.Log;
|
||||
|
||||
import de.danoeh.antennapod.model.feed.Feed;
|
||||
import de.danoeh.antennapod.model.feed.FeedItem;
|
||||
|
||||
import static de.danoeh.antennapod.model.feed.FeedPreferences.SPEED_USE_GLOBAL;
|
||||
|
@ -338,6 +339,10 @@ class DBUpgrader {
|
|||
db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
|
||||
+ " ADD COLUMN " + PodDBAdapter.KEY_FEED_SKIP_SILENCE + " INTEGER");
|
||||
}
|
||||
if (oldVersion < 3050000) {
|
||||
db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
|
||||
+ " ADD COLUMN " + PodDBAdapter.KEY_STATE + " INTEGER DEFAULT " + Feed.STATE_SUBSCRIBED);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import androidx.documentfile.provider.DocumentFile;
|
|||
import com.google.common.util.concurrent.Futures;
|
||||
import de.danoeh.antennapod.event.DownloadLogEvent;
|
||||
|
||||
import de.danoeh.antennapod.model.feed.FeedItemFilter;
|
||||
import de.danoeh.antennapod.net.download.serviceinterface.AutoDownloadManager;
|
||||
import de.danoeh.antennapod.net.download.serviceinterface.DownloadServiceInterface;
|
||||
import de.danoeh.antennapod.net.download.serviceinterface.FeedUpdateManager;
|
||||
|
@ -141,12 +142,13 @@ public class DBWriter {
|
|||
// Do full update of this feed to get rid of the item
|
||||
FeedUpdateManager.getInstance().runOnce(context, media.getItem().getFeed());
|
||||
} 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);
|
||||
if (media.getItem().getFeed().getState() == Feed.STATE_SUBSCRIBED) {
|
||||
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()));
|
||||
}
|
||||
|
@ -174,7 +176,7 @@ public class DBWriter {
|
|||
adapter.removeFeed(feed);
|
||||
adapter.close();
|
||||
|
||||
if (!feed.isLocalFeed()) {
|
||||
if (!feed.isLocalFeed() && feed.getState() == Feed.STATE_SUBSCRIBED) {
|
||||
SynchronizationQueueSink.enqueueFeedRemovedIfSynchronizationIsActive(context, feed.getDownloadUrl());
|
||||
}
|
||||
EventBus.getDefault().post(new FeedListUpdateEvent(feed));
|
||||
|
@ -786,7 +788,7 @@ public class DBWriter {
|
|||
adapter.close();
|
||||
|
||||
for (Feed feed : feeds) {
|
||||
if (!feed.isLocalFeed()) {
|
||||
if (!feed.isLocalFeed() && feed.getState() == Feed.STATE_SUBSCRIBED) {
|
||||
SynchronizationQueueSink.enqueueFeedAddedIfSynchronizationIsActive(context, feed.getDownloadUrl());
|
||||
}
|
||||
}
|
||||
|
@ -928,6 +930,32 @@ public class DBWriter {
|
|||
});
|
||||
}
|
||||
|
||||
public static Future<?> setFeedState(Context context, Feed feed, int newState) {
|
||||
int oldState = feed.getState();
|
||||
return runOnDbThread(() -> {
|
||||
PodDBAdapter adapter = PodDBAdapter.getInstance();
|
||||
adapter.open();
|
||||
adapter.setFeedState(feed.getId(), newState);
|
||||
feed.setState(newState);
|
||||
if (oldState == Feed.STATE_NOT_SUBSCRIBED && newState == Feed.STATE_SUBSCRIBED) {
|
||||
feed.getPreferences().setKeepUpdated(true);
|
||||
DBWriter.setFeedPreferences(feed.getPreferences());
|
||||
FeedUpdateManager.getInstance().runOnceOrAsk(context, feed);
|
||||
SynchronizationQueueSink.enqueueFeedAddedIfSynchronizationIsActive(context, feed.getDownloadUrl());
|
||||
DBReader.getFeedItemList(feed, FeedItemFilter.unfiltered(),
|
||||
SortOrder.DATE_NEW_OLD, 0, Integer.MAX_VALUE);
|
||||
for (FeedItem item : feed.getItems()) {
|
||||
if (item.isPlayed()) {
|
||||
SynchronizationQueueSink.enqueueEpisodePlayedIfSynchronizationIsActive(
|
||||
context, item.getMedia(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
adapter.close();
|
||||
EventBus.getDefault().post(new FeedListUpdateEvent(feed));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the FeedItems in the queue with the given the named sort order.
|
||||
*
|
||||
|
|
|
@ -157,7 +157,8 @@ public abstract class FeedDatabaseWriter {
|
|||
+ "\n\nNow the feed contains:\n" + duplicateEpisodeDetails(item)));
|
||||
oldItem.setItemIdentifier(item.getItemIdentifier());
|
||||
|
||||
if (oldItem.isPlayed() && oldItem.getMedia() != null) {
|
||||
if (oldItem.isPlayed() && oldItem.getMedia() != null
|
||||
&& savedFeed.getState() == Feed.STATE_SUBSCRIBED) {
|
||||
EpisodeAction action = new EpisodeAction.Builder(oldItem, EpisodeAction.PLAY)
|
||||
.currentTimestamp()
|
||||
.started(oldItem.getMedia().getDuration() / 1000)
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package de.danoeh.antennapod.storage.database;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
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.model.feed.SortOrder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class NonSubscribedFeedsCleaner {
|
||||
private static final String TAG = "NonSubscrFeedsCleaner";
|
||||
private static final long TIME_TO_KEEP = 1000L * 3600 * 24 * 30; // 30 days
|
||||
|
||||
public static void deleteOldNonSubscribedFeeds(Context context) {
|
||||
List<Feed> feeds = DBReader.getFeedList();
|
||||
for (Feed feed : feeds) {
|
||||
if (feed.getState() != Feed.STATE_NOT_SUBSCRIBED) {
|
||||
continue;
|
||||
}
|
||||
DBReader.getFeedItemList(feed, FeedItemFilter.unfiltered(), SortOrder.DATE_NEW_OLD, 0, Integer.MAX_VALUE);
|
||||
if (shouldDelete(feed)) {
|
||||
Log.d(TAG, "Deleting unsubscribed feed " + feed.getTitle());
|
||||
DBWriter.deleteFeed(context, feed.getId());
|
||||
}
|
||||
feed.setItems(null); // Let it be garbage collected
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean shouldDelete(Feed feed) {
|
||||
if (feed.getState() != Feed.STATE_NOT_SUBSCRIBED) {
|
||||
return false;
|
||||
} else if (feed.getItems() == null) {
|
||||
return false;
|
||||
}
|
||||
for (FeedItem item : feed.getItems()) {
|
||||
if (item.isTagged(FeedItem.TAG_FAVORITE)
|
||||
|| item.isTagged(FeedItem.TAG_QUEUE)
|
||||
|| item.isDownloaded()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return feed.getLastRefreshAttempt() < System.currentTimeMillis() - TIME_TO_KEEP;
|
||||
}
|
||||
}
|
|
@ -52,7 +52,7 @@ public class PodDBAdapter {
|
|||
|
||||
private static final String TAG = "PodDBAdapter";
|
||||
public static final String DATABASE_NAME = "Antennapod.db";
|
||||
public static final int VERSION = 3040000;
|
||||
public static final int VERSION = 3050000;
|
||||
|
||||
/**
|
||||
* Maximum number of arguments for IN-operator.
|
||||
|
@ -121,6 +121,7 @@ public class PodDBAdapter {
|
|||
public static final String KEY_EPISODE_NOTIFICATION = "episode_notification";
|
||||
public static final String KEY_NEW_EPISODES_ACTION = "new_episodes_action";
|
||||
public static final String KEY_PODCASTINDEX_CHAPTER_URL = "podcastindex_chapter_url";
|
||||
public static final String KEY_STATE = "state";
|
||||
|
||||
// Table names
|
||||
public static final String TABLE_NAME_FEEDS = "Feeds";
|
||||
|
@ -171,6 +172,7 @@ public class PodDBAdapter {
|
|||
+ KEY_FEED_SKIP_INTRO + " INTEGER DEFAULT 0,"
|
||||
+ KEY_FEED_SKIP_ENDING + " INTEGER DEFAULT 0,"
|
||||
+ KEY_EPISODE_NOTIFICATION + " INTEGER DEFAULT 0,"
|
||||
+ KEY_STATE + " INTEGER DEFAULT " + Feed.STATE_SUBSCRIBED + ","
|
||||
+ KEY_NEW_EPISODES_ACTION + " INTEGER DEFAULT 0)";
|
||||
|
||||
private static final String CREATE_TABLE_FEED_ITEMS = "CREATE TABLE "
|
||||
|
@ -323,6 +325,7 @@ public class PodDBAdapter {
|
|||
+ TABLE_NAME_FEEDS + "." + KEY_FEED_SKIP_INTRO + ", "
|
||||
+ TABLE_NAME_FEEDS + "." + KEY_FEED_SKIP_ENDING + ", "
|
||||
+ TABLE_NAME_FEEDS + "." + KEY_EPISODE_NOTIFICATION + ", "
|
||||
+ TABLE_NAME_FEEDS + "." + KEY_STATE + ", "
|
||||
+ TABLE_NAME_FEEDS + "." + KEY_NEW_EPISODES_ACTION;
|
||||
|
||||
private static final String JOIN_FEED_ITEM_AND_MEDIA = " LEFT JOIN " + TABLE_NAME_FEED_MEDIA
|
||||
|
@ -337,6 +340,9 @@ public class PodDBAdapter {
|
|||
"SELECT " + KEYS_FEED_ITEM_WITHOUT_DESCRIPTION + ", " + KEYS_FEED_MEDIA
|
||||
+ " FROM " + TABLE_NAME_FEED_ITEMS
|
||||
+ JOIN_FEED_ITEM_AND_MEDIA;
|
||||
public static final String SELECT_WHERE_FEED_IS_SUBSCRIBED = TABLE_NAME_FEED_ITEMS + "." + KEY_FEED
|
||||
+ " IN (SELECT " + KEY_ID + " FROM " + TABLE_NAME_FEEDS
|
||||
+ " WHERE " + KEY_STATE + "=" + Feed.STATE_SUBSCRIBED + ")";
|
||||
|
||||
private static Context context;
|
||||
private static PodDBAdapter instance;
|
||||
|
@ -431,6 +437,7 @@ public class PodDBAdapter {
|
|||
values.put(KEY_LASTUPDATE, feed.getLastModified());
|
||||
values.put(KEY_TYPE, feed.getType());
|
||||
values.put(KEY_FEED_IDENTIFIER, feed.getFeedIdentifier());
|
||||
values.put(KEY_STATE, feed.getState());
|
||||
|
||||
values.put(KEY_IS_PAGED, feed.isPaged());
|
||||
values.put(KEY_NEXT_PAGE_LINK, feed.getNextPageLink());
|
||||
|
@ -768,6 +775,12 @@ public class PodDBAdapter {
|
|||
db.update(TABLE_NAME_FEEDS, values, KEY_ID + "=?", new String[]{String.valueOf(feedId)});
|
||||
}
|
||||
|
||||
public void setFeedState(long feedId, int state) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(KEY_STATE, state);
|
||||
db.update(TABLE_NAME_FEEDS, values, KEY_ID + "=?", new String[]{String.valueOf(feedId)});
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts or updates a download status.
|
||||
*/
|
||||
|
@ -1101,6 +1114,7 @@ public class PodDBAdapter {
|
|||
// Hide episodes that have been played but not completed
|
||||
+ " AND (" + KEY_LAST_PLAYED_TIME + " == 0"
|
||||
+ " OR " + KEY_LAST_PLAYED_TIME + " > " + (System.currentTimeMillis() - 1000L * 3600L) + ")"
|
||||
+ " AND " + SELECT_WHERE_FEED_IS_SUBSCRIBED
|
||||
+ " ORDER BY " + randomEpisodeNumber(seed);
|
||||
final String query = "SELECT * FROM (" + allItemsRandomOrder + ")"
|
||||
+ " GROUP BY " + KEY_FEED
|
||||
|
@ -1221,6 +1235,7 @@ public class PodDBAdapter {
|
|||
+ JOIN_FEED_ITEM_AND_MEDIA
|
||||
+ " INNER JOIN " + TABLE_NAME_FEEDS
|
||||
+ " ON " + TABLE_NAME_FEED_ITEMS + "." + KEY_FEED + "=" + TABLE_NAME_FEEDS + "." + KEY_ID
|
||||
+ " WHERE " + TABLE_NAME_FEEDS + "." + KEY_STATE + "=" + Feed.STATE_SUBSCRIBED
|
||||
+ " GROUP BY " + TABLE_NAME_FEEDS + "." + KEY_ID;
|
||||
return db.rawQuery(query, null);
|
||||
}
|
||||
|
@ -1372,7 +1387,7 @@ public class PodDBAdapter {
|
|||
}
|
||||
|
||||
String queryStart = SELECT_FEED_ITEMS_AND_MEDIA_WITH_DESCRIPTION
|
||||
+ " WHERE " + queryFeedId + " AND (";
|
||||
+ " WHERE " + queryFeedId + " AND " + SELECT_WHERE_FEED_IS_SUBSCRIBED + " AND (";
|
||||
StringBuilder sb = new StringBuilder(queryStart);
|
||||
|
||||
for (int i = 0; i < queryWords.length; i++) {
|
||||
|
@ -1401,12 +1416,13 @@ public class PodDBAdapter {
|
|||
public Cursor searchFeeds(String searchQuery) {
|
||||
String[] queryWords = prepareSearchQuery(searchQuery);
|
||||
|
||||
String queryStart = "SELECT " + KEYS_FEED + " FROM " + TABLE_NAME_FEEDS + " WHERE ";
|
||||
String queryStart = "SELECT " + KEYS_FEED + " FROM " + TABLE_NAME_FEEDS
|
||||
+ " WHERE " + KEY_STATE + " = " + Feed.STATE_SUBSCRIBED;
|
||||
StringBuilder sb = new StringBuilder(queryStart);
|
||||
|
||||
for (int i = 0; i < queryWords.length; i++) {
|
||||
sb
|
||||
.append("(")
|
||||
.append(" AND (")
|
||||
.append(KEY_TITLE).append(" LIKE '%").append(queryWords[i])
|
||||
.append("%' OR ")
|
||||
.append(KEY_CUSTOM_TITLE).append(" LIKE '%").append(queryWords[i])
|
||||
|
@ -1415,13 +1431,9 @@ public class PodDBAdapter {
|
|||
.append("%' OR ")
|
||||
.append(KEY_DESCRIPTION).append(" LIKE '%").append(queryWords[i])
|
||||
.append("%') ");
|
||||
|
||||
if (i != queryWords.length - 1) {
|
||||
sb.append("AND ");
|
||||
}
|
||||
}
|
||||
|
||||
sb.append("ORDER BY " + KEY_TITLE + " ASC LIMIT 300");
|
||||
sb.append(" ORDER BY " + KEY_TITLE + " ASC LIMIT 300");
|
||||
|
||||
return db.rawQuery(sb.toString(), null);
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ public class FeedCursor extends CursorWrapper {
|
|||
private final int indexSortOrder;
|
||||
private final int indexLastUpdateFailed;
|
||||
private final int indexImageUrl;
|
||||
private final int indexState;
|
||||
|
||||
public FeedCursor(Cursor cursor) {
|
||||
super(new FeedPreferencesCursor(cursor));
|
||||
|
@ -58,6 +59,7 @@ public class FeedCursor extends CursorWrapper {
|
|||
indexSortOrder = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_SORT_ORDER);
|
||||
indexLastUpdateFailed = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_LAST_UPDATE_FAILED);
|
||||
indexImageUrl = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_IMAGE_URL);
|
||||
indexState = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_STATE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -85,7 +87,8 @@ public class FeedCursor extends CursorWrapper {
|
|||
getString(indexNextPageLink),
|
||||
getString(indexHide),
|
||||
SortOrder.fromCodeString(getString(indexSortOrder)),
|
||||
getInt(indexLastUpdateFailed) > 0);
|
||||
getInt(indexLastUpdateFailed) > 0,
|
||||
getInt(indexState));
|
||||
feed.setPreferences(preferencesCursor.getFeedPreferences());
|
||||
return feed;
|
||||
}
|
||||
|
|
|
@ -66,6 +66,9 @@ public class FeedItemFilterQuery {
|
|||
if (filter.showInHistory) {
|
||||
statements.add(keyCompletionDate + " > 0 ");
|
||||
}
|
||||
if (!filter.includeNotSubscribed) {
|
||||
statements.add(PodDBAdapter.SELECT_WHERE_FEED_IS_SUBSCRIBED);
|
||||
}
|
||||
|
||||
if (statements.isEmpty()) {
|
||||
return "";
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
package de.danoeh.antennapod.storage.database;
|
||||
|
||||
import de.danoeh.antennapod.model.feed.Feed;
|
||||
import de.danoeh.antennapod.model.feed.FeedItem;
|
||||
import de.danoeh.antennapod.model.feed.FeedMedia;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class NonSubscribedFeedsCleanerTest {
|
||||
|
||||
@Test
|
||||
public void testSubscribed() {
|
||||
Feed feed = createFeed();
|
||||
feed.setLastRefreshAttempt(System.currentTimeMillis() - TimeUnit.MILLISECONDS.convert(200, TimeUnit.DAYS));
|
||||
assertFalse(NonSubscribedFeedsCleaner.shouldDelete(feed));
|
||||
|
||||
feed.setState(Feed.STATE_NOT_SUBSCRIBED);
|
||||
assertTrue(NonSubscribedFeedsCleaner.shouldDelete(feed));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOldDate() {
|
||||
Feed feed = createFeed();
|
||||
feed.setState(Feed.STATE_NOT_SUBSCRIBED);
|
||||
feed.setLastRefreshAttempt(System.currentTimeMillis());
|
||||
assertFalse(NonSubscribedFeedsCleaner.shouldDelete(feed));
|
||||
|
||||
feed.setLastRefreshAttempt(System.currentTimeMillis() - TimeUnit.MILLISECONDS.convert(10, TimeUnit.DAYS));
|
||||
assertFalse(NonSubscribedFeedsCleaner.shouldDelete(feed));
|
||||
|
||||
feed.setLastRefreshAttempt(System.currentTimeMillis() - TimeUnit.MILLISECONDS.convert(200, TimeUnit.DAYS));
|
||||
assertTrue(NonSubscribedFeedsCleaner.shouldDelete(feed));
|
||||
|
||||
feed.setLastRefreshAttempt(System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(200, TimeUnit.DAYS));
|
||||
assertFalse(NonSubscribedFeedsCleaner.shouldDelete(feed));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueuedItem() {
|
||||
Feed feed = createFeed();
|
||||
feed.setState(Feed.STATE_NOT_SUBSCRIBED);
|
||||
feed.setLastRefreshAttempt(System.currentTimeMillis() - TimeUnit.MILLISECONDS.convert(200, TimeUnit.DAYS));
|
||||
feed.getItems().add(createItem(feed));
|
||||
assertTrue(NonSubscribedFeedsCleaner.shouldDelete(feed));
|
||||
|
||||
FeedItem queuedItem = createItem(feed);
|
||||
queuedItem.addTag(FeedItem.TAG_QUEUE);
|
||||
feed.getItems().add(queuedItem);
|
||||
assertFalse(NonSubscribedFeedsCleaner.shouldDelete(feed));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFavoriteItem() {
|
||||
Feed feed = createFeed();
|
||||
feed.setState(Feed.STATE_NOT_SUBSCRIBED);
|
||||
feed.setLastRefreshAttempt(System.currentTimeMillis() - TimeUnit.MILLISECONDS.convert(200, TimeUnit.DAYS));
|
||||
feed.getItems().add(createItem(feed));
|
||||
assertTrue(NonSubscribedFeedsCleaner.shouldDelete(feed));
|
||||
|
||||
FeedItem queuedItem = createItem(feed);
|
||||
queuedItem.addTag(FeedItem.TAG_FAVORITE);
|
||||
feed.getItems().add(queuedItem);
|
||||
assertFalse(NonSubscribedFeedsCleaner.shouldDelete(feed));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDownloadedItem() {
|
||||
Feed feed = createFeed();
|
||||
feed.setState(Feed.STATE_NOT_SUBSCRIBED);
|
||||
feed.setLastRefreshAttempt(System.currentTimeMillis() - TimeUnit.MILLISECONDS.convert(200, TimeUnit.DAYS));
|
||||
feed.getItems().add(createItem(feed));
|
||||
assertTrue(NonSubscribedFeedsCleaner.shouldDelete(feed));
|
||||
|
||||
FeedItem queuedItem = createItem(feed);
|
||||
queuedItem.getMedia().setDownloaded(true, System.currentTimeMillis());
|
||||
feed.getItems().add(queuedItem);
|
||||
assertFalse(NonSubscribedFeedsCleaner.shouldDelete(feed));
|
||||
}
|
||||
|
||||
private Feed createFeed() {
|
||||
Feed feed = new Feed(0, null, "title", "http://example.com", "This is the description",
|
||||
"http://example.com/payment", "Daniel", "en", null, "http://example.com/feed",
|
||||
"http://example.com/image", null, "http://example.com/feed", System.currentTimeMillis());
|
||||
feed.setItems(new ArrayList<>());
|
||||
return feed;
|
||||
}
|
||||
|
||||
private FeedItem createItem(Feed feed) {
|
||||
FeedItem item = new FeedItem(0, "Item", "ItemId", "url", new Date(), FeedItem.PLAYED, feed);
|
||||
FeedMedia media = new FeedMedia(item, "http://download.url.net/", 1234567, "audio/mpeg");
|
||||
media.setId(item.getId());
|
||||
item.setMedia(media);
|
||||
return item;
|
||||
}
|
||||
}
|
|
@ -28,6 +28,9 @@ public class HtmlWriter {
|
|||
|
||||
writer.append(templateParts[0]);
|
||||
for (Feed feed : feeds) {
|
||||
if (feed.getState() != Feed.STATE_SUBSCRIBED) {
|
||||
continue;
|
||||
}
|
||||
writer.append("<li><div><img src=\"");
|
||||
writer.append(feed.getImageUrl());
|
||||
writer.append("\" /><p>");
|
||||
|
|
|
@ -48,6 +48,9 @@ public class OpmlWriter {
|
|||
|
||||
xs.startTag(null, OpmlSymbols.BODY);
|
||||
for (Feed feed : feeds) {
|
||||
if (feed.getState() != Feed.STATE_SUBSCRIBED) {
|
||||
continue;
|
||||
}
|
||||
xs.startTag(null, OpmlSymbols.OUTLINE);
|
||||
xs.attribute(null, OpmlSymbols.TEXT, feed.getTitle());
|
||||
xs.attribute(null, OpmlSymbols.TITLE, feed.getTitle());
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt">
|
||||
<stroke
|
||||
android:width="2dp"
|
||||
android:color="?attr/colorPrimary" />
|
||||
|
||||
<corners android:radius="8dp" />
|
||||
|
||||
<solid>
|
||||
<aapt:attr name="android:color" >
|
||||
<selector>
|
||||
<item android:alpha="0.1" android:color="?attr/colorPrimary" />
|
||||
</selector>
|
||||
</aapt:attr>
|
||||
</solid>
|
||||
</shape>
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<corners android:radius="8dp" />
|
||||
</shape>
|
|
@ -0,0 +1,11 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M17.656,6.343L12,12M12,12L6.343,17.656M12,12L17.656,17.656M12,12L6.343,6.343"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="?attr/action_icon_color"/>
|
||||
</vector>
|
|
@ -8,7 +8,6 @@
|
|||
<color name="medium_gray">#afafaf</color>
|
||||
<color name="black">#000000</color>
|
||||
<color name="image_readability_tint">#80000000</color>
|
||||
<color name="feed_image_bg">#50000000</color>
|
||||
<color name="feed_text_bg">#55333333</color>
|
||||
|
||||
<!-- Theme colors -->
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
<dimen name="text_size_large">22sp</dimen>
|
||||
<dimen name="thumbnail_length_itemlist">56dp</dimen>
|
||||
<dimen name="thumbnail_length_queue_item">56dp</dimen>
|
||||
<dimen name="thumbnail_length_onlinefeedview">92dp</dimen>
|
||||
<dimen name="feeditemlist_header_height">132dp</dimen>
|
||||
<dimen name="thumbnail_length_navlist">40dp</dimen>
|
||||
<dimen name="listitem_iconwithtext_height">48dp</dimen>
|
||||
<dimen name="listitem_iconwithtext_textleftpadding">16dp</dimen>
|
||||
|
|
|
@ -681,9 +681,8 @@
|
|||
|
||||
<!-- Online feed view -->
|
||||
<string name="subscribe_label">Subscribe</string>
|
||||
<string name="subscribing_label">Subscribing…</string>
|
||||
<string name="preview_episode">Preview</string>
|
||||
<string name="stop_preview">Stop preview</string>
|
||||
<string name="preview_episodes">Episodes preview</string>
|
||||
<string name="state_deleted_not_subscribed">Playback state and history of non-subscribed podcasts are deleted after a while. Subscribe to keep them.</string>
|
||||
|
||||
<!-- Content descriptions for image buttons -->
|
||||
<string name="toolbar_back_button_content_description">Back</string>
|
||||
|
|
Loading…
Reference in New Issue