From 73a6ff1f6003c776fb37bd8e7315045e69e710d2 Mon Sep 17 00:00:00 2001 From: GitStart <1501599+gitstart@users.noreply.github.com> Date: Sat, 28 Jan 2023 16:53:21 +0500 Subject: [PATCH] Remove subscribed podcasts from discover / suggestions (#6269) --- .../fragment/DiscoveryFragment.java | 53 +++++++------- .../fragment/QuickFeedDiscoveryFragment.java | 55 ++++++++------- .../net/discovery/ItunesTopListLoader.java | 70 +++++++++++-------- 3 files changed, 100 insertions(+), 78 deletions(-) diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/DiscoveryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DiscoveryFragment.java index 88b9ac8f1..16ccb2af4 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/DiscoveryFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/DiscoveryFragment.java @@ -14,17 +14,25 @@ import android.widget.Button; import android.widget.GridView; import android.widget.ProgressBar; import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.Fragment; - import com.google.android.material.appbar.MaterialToolbar; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.textfield.MaterialAutoCompleteTextView; import com.google.android.material.textfield.TextInputLayout; - +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.OnlineFeedViewActivity; +import de.danoeh.antennapod.adapter.itunes.ItunesAdapter; import de.danoeh.antennapod.core.BuildConfig; +import de.danoeh.antennapod.core.storage.DBReader; +import de.danoeh.antennapod.event.DiscoveryDefaultUpdateEvent; +import de.danoeh.antennapod.net.discovery.ItunesTopListLoader; +import de.danoeh.antennapod.net.discovery.PodcastSearchResult; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; import org.greenrobot.eventbus.EventBus; import java.util.ArrayList; @@ -35,20 +43,13 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import de.danoeh.antennapod.R; -import de.danoeh.antennapod.activity.OnlineFeedViewActivity; -import de.danoeh.antennapod.adapter.itunes.ItunesAdapter; -import de.danoeh.antennapod.event.DiscoveryDefaultUpdateEvent; -import de.danoeh.antennapod.net.discovery.ItunesTopListLoader; -import de.danoeh.antennapod.net.discovery.PodcastSearchResult; -import io.reactivex.disposables.Disposable; - /** * Searches iTunes store for top podcasts and displays results in a list. */ public class DiscoveryFragment extends Fragment implements Toolbar.OnMenuItemClickListener { private static final String TAG = "ItunesSearchFragment"; + private static final int NUM_OF_TOP_PODCASTS = 25; private SharedPreferences prefs; /** @@ -188,19 +189,23 @@ public class DiscoveryFragment extends Fragment implements Toolbar.OnMenuItemCli } ItunesTopListLoader loader = new ItunesTopListLoader(getContext()); - disposable = loader.loadToplist(country, 25).subscribe( - podcasts -> { - progressBar.setVisibility(View.GONE); - topList = podcasts; - updateData(topList); - }, error -> { - Log.e(TAG, Log.getStackTraceString(error)); - progressBar.setVisibility(View.GONE); - txtvError.setText(error.getMessage()); - txtvError.setVisibility(View.VISIBLE); - butRetry.setOnClickListener(v -> loadToplist(country)); - butRetry.setVisibility(View.VISIBLE); - }); + disposable = Observable.fromCallable(() -> + loader.loadToplist(country, NUM_OF_TOP_PODCASTS, DBReader.getFeedList())) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + podcasts -> { + progressBar.setVisibility(View.GONE); + topList = podcasts; + updateData(topList); + }, error -> { + Log.e(TAG, Log.getStackTraceString(error)); + progressBar.setVisibility(View.GONE); + txtvError.setText(error.getMessage()); + txtvError.setVisibility(View.VISIBLE); + butRetry.setOnClickListener(v -> loadToplist(country)); + butRetry.setVisibility(View.VISIBLE); + }); } @Override diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java index acdd1e1c7..d5192061c 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java @@ -5,7 +5,6 @@ import android.content.SharedPreferences; import android.os.Bundle; import android.text.TextUtils; import android.util.DisplayMetrics; -import androidx.fragment.app.Fragment; import android.util.Log; import android.view.LayoutInflater; @@ -16,20 +15,23 @@ import android.widget.Button; import android.widget.GridView; import android.widget.LinearLayout; import android.widget.TextView; - +import androidx.fragment.app.Fragment; import de.danoeh.antennapod.BuildConfig; -import de.danoeh.antennapod.net.discovery.ItunesTopListLoader; -import de.danoeh.antennapod.net.discovery.PodcastSearchResult; -import org.greenrobot.eventbus.EventBus; -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; - import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.activity.OnlineFeedViewActivity; import de.danoeh.antennapod.adapter.FeedDiscoverAdapter; +import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.event.DiscoveryDefaultUpdateEvent; +import de.danoeh.antennapod.net.discovery.ItunesTopListLoader; +import de.danoeh.antennapod.net.discovery.PodcastSearchResult; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; import java.util.ArrayList; import java.util.List; @@ -138,26 +140,29 @@ public class QuickFeedDiscoveryFragment extends Fragment implements AdapterView. return; } - disposable = loader.loadToplist(countryCode, NUM_SUGGESTIONS) + disposable = Observable.fromCallable(() -> + loader.loadToplist(countryCode, NUM_SUGGESTIONS, DBReader.getFeedList())) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) .subscribe( - podcasts -> { - errorView.setVisibility(View.GONE); - if (podcasts.size() == 0) { - errorTextView.setText(getResources().getText(R.string.search_status_no_results)); - errorView.setVisibility(View.VISIBLE); - discoverGridLayout.setVisibility(View.INVISIBLE); - } else { - discoverGridLayout.setVisibility(View.VISIBLE); - adapter.updateData(podcasts); - } - }, error -> { - Log.e(TAG, Log.getStackTraceString(error)); - errorTextView.setText(error.getLocalizedMessage()); + podcasts -> { + errorView.setVisibility(View.GONE); + if (podcasts.size() == 0) { + errorTextView.setText(getResources().getText(R.string.search_status_no_results)); errorView.setVisibility(View.VISIBLE); discoverGridLayout.setVisibility(View.INVISIBLE); - errorRetry.setVisibility(View.VISIBLE); - errorRetry.setOnClickListener((listener) -> loadToplist()); - }); + } else { + discoverGridLayout.setVisibility(View.VISIBLE); + adapter.updateData(podcasts); + } + }, error -> { + Log.e(TAG, Log.getStackTraceString(error)); + errorTextView.setText(error.getLocalizedMessage()); + errorView.setVisibility(View.VISIBLE); + discoverGridLayout.setVisibility(View.INVISIBLE); + errorRetry.setVisibility(View.VISIBLE); + errorRetry.setOnClickListener(v -> loadToplist()); + }); } @Override diff --git a/net/discovery/src/main/java/de/danoeh/antennapod/net/discovery/ItunesTopListLoader.java b/net/discovery/src/main/java/de/danoeh/antennapod/net/discovery/ItunesTopListLoader.java index 53ea00235..0bfc5a863 100644 --- a/net/discovery/src/main/java/de/danoeh/antennapod/net/discovery/ItunesTopListLoader.java +++ b/net/discovery/src/main/java/de/danoeh/antennapod/net/discovery/ItunesTopListLoader.java @@ -2,12 +2,8 @@ package de.danoeh.antennapod.net.discovery; import android.content.Context; import android.util.Log; - import de.danoeh.antennapod.core.service.download.AntennapodHttpClient; -import io.reactivex.Single; -import io.reactivex.SingleOnSubscribe; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.schedulers.Schedulers; +import de.danoeh.antennapod.model.feed.Feed; import okhttp3.CacheControl; import okhttp3.OkHttpClient; import okhttp3.Request; @@ -18,8 +14,10 @@ import org.json.JSONObject; import java.io.IOException; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Set; import java.util.concurrent.TimeUnit; public class ItunesTopListLoader { @@ -30,39 +28,53 @@ public class ItunesTopListLoader { public static final String PREF_KEY_NEEDS_CONFIRM = "needs_confirm"; public static final String PREFS = "CountryRegionPrefs"; public static final String COUNTRY_CODE_UNSET = "99"; + private static final int NUM_LOADED = 25; public ItunesTopListLoader(Context context) { this.context = context; } - public Single> loadToplist(String country, int limit) { - return Single.create((SingleOnSubscribe>) emitter -> { - OkHttpClient client = AntennapodHttpClient.getHttpClient(); - String feedString; - String loadCountry = country; + public List loadToplist(String country, int limit, List subscribed) + throws JSONException, IOException { + OkHttpClient client = AntennapodHttpClient.getHttpClient(); + String feedString; + String loadCountry = country; + if (COUNTRY_CODE_UNSET.equals(country)) { + loadCountry = Locale.getDefault().getCountry(); + } + try { + feedString = getTopListFeed(client, loadCountry); + } catch (IOException e) { if (COUNTRY_CODE_UNSET.equals(country)) { - loadCountry = Locale.getDefault().getCountry(); + feedString = getTopListFeed(client, "US"); + } else { + throw e; } - try { - feedString = getTopListFeed(client, loadCountry, limit); - } catch (IOException e) { - if (COUNTRY_CODE_UNSET.equals(country)) { - feedString = getTopListFeed(client, "US", limit); - } else { - emitter.onError(e); - return; - } - } - - List podcasts = parseFeed(feedString); - emitter.onSuccess(podcasts); - }) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()); + } + return removeSubscribed(parseFeed(feedString), subscribed, limit); } - private String getTopListFeed(OkHttpClient client, String country, int limit) throws IOException { - String url = "https://itunes.apple.com/%s/rss/toppodcasts/limit=" + limit + "/explicit=true/json"; + private static List removeSubscribed( + List suggestedPodcasts, List subscribedFeeds, int limit) { + Set subscribedPodcastsSet = new HashSet<>(); + for (Feed subscribedFeed : subscribedFeeds) { + String subscribedTitle = subscribedFeed.getTitle().trim() + " - " + subscribedFeed.getAuthor().trim(); + subscribedPodcastsSet.add(subscribedTitle); + } + List suggestedNotSubscribed = new ArrayList<>(); + for (PodcastSearchResult suggested : suggestedPodcasts) { + if (!subscribedPodcastsSet.contains(suggested.title.trim())) { + suggestedNotSubscribed.add(suggested); + } + if (suggestedNotSubscribed.size() == limit) { + return suggestedNotSubscribed; + } + } + return suggestedNotSubscribed; + } + + private String getTopListFeed(OkHttpClient client, String country) throws IOException { + String url = "https://itunes.apple.com/%s/rss/toppodcasts/limit=" + NUM_LOADED + "/explicit=true/json"; Log.d(TAG, "Feed URL " + String.format(url, country)); Request.Builder httpReq = new Request.Builder() .cacheControl(new CacheControl.Builder().maxStale(1, TimeUnit.DAYS).build())