diff options
author | H. Lehmann <ByteHamster@users.noreply.github.com> | 2020-04-10 16:14:14 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-10 16:14:14 +0200 |
commit | 4959319d580eec9eac18c4cf4197409d4c2f0545 (patch) | |
tree | 43c9008fedca2820c6242f23212f6bd0d97fa82a /app/src/main/java/de/danoeh | |
parent | a0c7e1799660cd0505b86e75e66527155f695bbd (diff) | |
parent | ac13b7aa7b14e5e46971748ba2751c8bfa2bcf15 (diff) | |
download | AntennaPod-4959319d580eec9eac18c4cf4197409d4c2f0545.zip |
Merge pull request #4029 from ByteHamster/podcast-searcher-abstract
Abstract from iTunes search provider lookup
Diffstat (limited to 'app/src/main/java/de/danoeh')
14 files changed, 312 insertions, 439 deletions
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java index fd4e31074..a0d1c17d5 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java @@ -57,6 +57,7 @@ import de.danoeh.antennapod.core.util.URLChecker; import de.danoeh.antennapod.core.util.syndication.FeedDiscoverer; import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText; import de.danoeh.antennapod.dialog.AuthenticationDialog; +import de.danoeh.antennapod.discovery.PodcastSearcherRegistry; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; @@ -84,7 +85,6 @@ public class OnlineFeedViewActivity extends AppCompatActivity { public static final String ARG_FEEDURL = "arg.feedurl"; // Optional argument: specify a title for the actionbar. - public static final String ARG_TITLE = "title"; private static final int RESULT_ERROR = 2; private static final String TAG = "OnlineFeedViewActivity"; private volatile List<Feed> feeds; @@ -127,11 +127,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity { if (feedUrl == null) { Log.e(TAG, "feedUrl is null."); - new AlertDialog.Builder(OnlineFeedViewActivity.this) - .setNeutralButton(android.R.string.ok, (dialog, which) -> finish()) - .setTitle(R.string.error_label) - .setMessage(R.string.null_value_podcast_error) - .show(); + showNoPodcastFoundError(); } else { Log.d(TAG, "Activity was started with url " + feedUrl); setLoadingLayout(); @@ -140,13 +136,26 @@ public class OnlineFeedViewActivity extends AppCompatActivity { feedUrl = feedUrl.replaceFirst("((www.)?(subscribeonandroid.com/))", ""); } if (savedInstanceState == null) { - startFeedDownload(feedUrl, null, null); + lookupUrlAndDownload(feedUrl, null, null); } else { - startFeedDownload(feedUrl, savedInstanceState.getString("username"), savedInstanceState.getString("password")); + lookupUrlAndDownload(feedUrl, savedInstanceState.getString("username"), + savedInstanceState.getString("password")); } } } + private void showNoPodcastFoundError() { + new AlertDialog.Builder(OnlineFeedViewActivity.this) + .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(); + }) + .show(); + } + /** * Displays a progress indicator. */ @@ -198,10 +207,9 @@ public class OnlineFeedViewActivity extends AppCompatActivity { } } - private void resetIntent(String url, String title) { + private void resetIntent(String url) { Intent intent = new Intent(); intent.putExtra(ARG_FEEDURL, url); - intent.putExtra(ARG_TITLE, title); setIntent(intent); } @@ -226,6 +234,17 @@ public class OnlineFeedViewActivity extends AppCompatActivity { return super.onOptionsItemSelected(item); } + private void lookupUrlAndDownload(String url, String username, String password) { + download = PodcastSearcherRegistry.lookupUrl(url) + .subscribeOn(Schedulers.io()) + .observeOn(Schedulers.io()) + .subscribe(lookedUpUrl -> startFeedDownload(lookedUpUrl, username, password), + error -> { + showNoPodcastFoundError(); + Log.e(TAG, Log.getStackTraceString(error)); + }); + } + private void startFeedDownload(String url, String username, String password) { Log.d(TAG, "Starting feed download"); url = URLChecker.prepareURL(url); @@ -580,7 +599,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity { if (urls.size() == 1) { // Skip dialog and display the item directly - resetIntent(urls.get(0), titles.get(0)); + resetIntent(urls.get(0)); startFeedDownload(urls.get(0), null, null); return true; } @@ -589,7 +608,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity { DialogInterface.OnClickListener onClickListener = (dialog, which) -> { String selectedUrl = urls.get(which); dialog.dismiss(); - resetIntent(selectedUrl, titles.get(which)); + resetIntent(selectedUrl); FeedPreferences prefs = feed.getPreferences(); if(prefs != null) { startFeedDownload(selectedUrl, prefs.getUsername(), prefs.getPassword()); diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/CombinedSearcher.java b/app/src/main/java/de/danoeh/antennapod/discovery/CombinedSearcher.java index e34d8539c..6c2a87c12 100644 --- a/app/src/main/java/de/danoeh/antennapod/discovery/CombinedSearcher.java +++ b/app/src/main/java/de/danoeh/antennapod/discovery/CombinedSearcher.java @@ -1,8 +1,7 @@ package de.danoeh.antennapod.discovery; -import android.content.Context; +import android.text.TextUtils; import android.util.Log; -import android.util.Pair; import io.reactivex.Single; import io.reactivex.SingleOnSubscribe; import io.reactivex.android.schedulers.AndroidSchedulers; @@ -19,25 +18,22 @@ import java.util.concurrent.CountDownLatch; public class CombinedSearcher implements PodcastSearcher { private static final String TAG = "CombinedSearcher"; - private final List<Pair<PodcastSearcher, Float>> searchProviders = new ArrayList<>(); - - public CombinedSearcher(Context context) { - addProvider(new FyydPodcastSearcher(), 1.f); - addProvider(new ItunesPodcastSearcher(context), 1.f); - //addProvider(new GpodnetPodcastSearcher(), 0.6f); - } - - private void addProvider(PodcastSearcher provider, float priority) { - searchProviders.add(new Pair<>(provider, priority)); + public CombinedSearcher() { } public Single<List<PodcastSearchResult>> search(String query) { ArrayList<Disposable> disposables = new ArrayList<>(); - List<List<PodcastSearchResult>> singleResults = new ArrayList<>(Collections.nCopies(searchProviders.size(), null)); - CountDownLatch latch = new CountDownLatch(searchProviders.size()); - for (int i = 0; i < searchProviders.size(); i++) { - Pair<PodcastSearcher, Float> searchProviderInfo = searchProviders.get(i); - PodcastSearcher searcher = searchProviderInfo.first; + List<List<PodcastSearchResult>> singleResults = new ArrayList<>( + Collections.nCopies(PodcastSearcherRegistry.getSearchProviders().size(), null)); + CountDownLatch latch = new CountDownLatch(PodcastSearcherRegistry.getSearchProviders().size()); + for (int i = 0; i < PodcastSearcherRegistry.getSearchProviders().size(); i++) { + PodcastSearcherRegistry.SearcherInfo searchProviderInfo + = PodcastSearcherRegistry.getSearchProviders().get(i); + PodcastSearcher searcher = searchProviderInfo.searcher; + if (searchProviderInfo.weight <= 0.00001f || searcher.getClass() == CombinedSearcher.class) { + latch.countDown(); + continue; + } final int index = i; disposables.add(searcher.search(query).subscribe(e -> { singleResults.set(index, e); @@ -56,7 +52,9 @@ public class CombinedSearcher implements PodcastSearcher { }) .doOnDispose(() -> { for (Disposable disposable : disposables) { - disposable.dispose(); + if (disposable != null) { + disposable.dispose(); + } } }) .subscribeOn(Schedulers.io()) @@ -67,7 +65,7 @@ public class CombinedSearcher implements PodcastSearcher { HashMap<String, Float> resultRanking = new HashMap<>(); HashMap<String, PodcastSearchResult> urlToResult = new HashMap<>(); for (int i = 0; i < singleResults.size(); i++) { - float providerPriority = searchProviders.get(i).second; + float providerPriority = PodcastSearcherRegistry.getSearchProviders().get(i).weight; List<PodcastSearchResult> providerResults = singleResults.get(i); if (providerResults == null) { continue; @@ -93,4 +91,28 @@ public class CombinedSearcher implements PodcastSearcher { } return results; } + + @Override + public Single<String> lookupUrl(String url) { + return PodcastSearcherRegistry.lookupUrl(url); + } + + @Override + public boolean urlNeedsLookup(String url) { + return PodcastSearcherRegistry.urlNeedsLookup(url); + } + + @Override + public String getName() { + ArrayList<String> names = new ArrayList<>(); + for (int i = 0; i < PodcastSearcherRegistry.getSearchProviders().size(); i++) { + PodcastSearcherRegistry.SearcherInfo searchProviderInfo + = PodcastSearcherRegistry.getSearchProviders().get(i); + PodcastSearcher searcher = searchProviderInfo.searcher; + if (searchProviderInfo.weight > 0.00001f && searcher.getClass() != CombinedSearcher.class) { + names.add(searcher.getName()); + } + } + return TextUtils.join(", ", names); + } } diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/FyydPodcastSearcher.java b/app/src/main/java/de/danoeh/antennapod/discovery/FyydPodcastSearcher.java index 529a9e3d5..5a93e6530 100644 --- a/app/src/main/java/de/danoeh/antennapod/discovery/FyydPodcastSearcher.java +++ b/app/src/main/java/de/danoeh/antennapod/discovery/FyydPodcastSearcher.java @@ -35,4 +35,19 @@ public class FyydPodcastSearcher implements PodcastSearcher { .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); } + + @Override + public Single<String> lookupUrl(String url) { + return Single.just(url); + } + + @Override + public boolean urlNeedsLookup(String url) { + return false; + } + + @Override + public String getName() { + return "Fyyd"; + } } diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java b/app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java index 4fbac19ff..53237579f 100644 --- a/app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java +++ b/app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java @@ -33,4 +33,19 @@ public class GpodnetPodcastSearcher implements PodcastSearcher { .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); } + + @Override + public Single<String> lookupUrl(String url) { + return Single.just(url); + } + + @Override + public boolean urlNeedsLookup(String url) { + return false; + } + + @Override + public String getName() { + return "Gpodder.net"; + } } diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/ItunesPodcastSearcher.java b/app/src/main/java/de/danoeh/antennapod/discovery/ItunesPodcastSearcher.java index 620b30177..796ec556f 100644 --- a/app/src/main/java/de/danoeh/antennapod/discovery/ItunesPodcastSearcher.java +++ b/app/src/main/java/de/danoeh/antennapod/discovery/ItunesPodcastSearcher.java @@ -2,7 +2,6 @@ package de.danoeh.antennapod.discovery; import android.content.Context; import de.danoeh.antennapod.R; -import de.danoeh.antennapod.core.ClientConfig; import de.danoeh.antennapod.core.service.download.AntennapodHttpClient; import io.reactivex.Single; import io.reactivex.SingleOnSubscribe; @@ -23,12 +22,11 @@ import java.util.List; public class ItunesPodcastSearcher implements PodcastSearcher { private static final String ITUNES_API_URL = "https://itunes.apple.com/search?media=podcast&term=%s"; - private final Context context; - public ItunesPodcastSearcher(Context context) { - this.context = context; + public ItunesPodcastSearcher() { } + @Override public Single<List<PodcastSearchResult>> search(String query) { return Single.create((SingleOnSubscribe<List<PodcastSearchResult>>) subscriber -> { String encodedQuery; @@ -56,11 +54,12 @@ public class ItunesPodcastSearcher implements PodcastSearcher { for (int i = 0; i < j.length(); i++) { JSONObject podcastJson = j.getJSONObject(i); PodcastSearchResult podcast = PodcastSearchResult.fromItunes(podcastJson); - podcasts.add(podcast); + if (podcast.feedUrl != null) { + podcasts.add(podcast); + } } } else { - String prefix = context.getString(R.string.error_msg_prefix); - subscriber.onError(new IOException(prefix + response)); + subscriber.onError(new IOException(response.toString())); } } catch (IOException | JSONException e) { subscriber.onError(e); @@ -70,4 +69,36 @@ public class ItunesPodcastSearcher implements PodcastSearcher { .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); } + + @Override + public Single<String> lookupUrl(String url) { + return Single.create(emitter -> { + OkHttpClient client = AntennapodHttpClient.getHttpClient(); + Request.Builder httpReq = new Request.Builder().url(url); + try { + Response response = client.newCall(httpReq.build()).execute(); + if (response.isSuccessful()) { + String resultString = response.body().string(); + JSONObject result = new JSONObject(resultString); + JSONObject results = result.getJSONArray("results").getJSONObject(0); + String feedUrl = results.getString("feedUrl"); + emitter.onSuccess(feedUrl); + } else { + emitter.onError(new IOException(response.toString())); + } + } catch (IOException | JSONException e) { + emitter.onError(e); + } + }); + } + + @Override + public boolean urlNeedsLookup(String url) { + return url.contains("itunes.apple.com"); + } + + @Override + public String getName() { + return "iTunes"; + } } diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java b/app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java index 226b18204..e93a89ef0 100644 --- a/app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java +++ b/app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java @@ -4,7 +4,6 @@ import android.content.Context; import android.util.Log; import de.danoeh.antennapod.R; -import de.danoeh.antennapod.core.ClientConfig; import de.danoeh.antennapod.core.service.download.AntennapodHttpClient; import io.reactivex.Single; import io.reactivex.SingleOnSubscribe; @@ -49,35 +48,6 @@ public class ItunesTopListLoader { .observeOn(AndroidSchedulers.mainThread()); } - public Single<String> getFeedUrl(PodcastSearchResult podcast) { - if (!podcast.feedUrl.contains("itunes.apple.com")) { - return Single.just(podcast.feedUrl) - .observeOn(AndroidSchedulers.mainThread()); - } - return Single.create((SingleOnSubscribe<String>) emitter -> { - OkHttpClient client = AntennapodHttpClient.getHttpClient(); - Request.Builder httpReq = new Request.Builder() - .url(podcast.feedUrl); - try { - Response response = client.newCall(httpReq.build()).execute(); - if (response.isSuccessful()) { - String resultString = response.body().string(); - JSONObject result = new JSONObject(resultString); - JSONObject results = result.getJSONArray("results").getJSONObject(0); - String feedUrl = results.getString("feedUrl"); - emitter.onSuccess(feedUrl); - } else { - String prefix = context.getString(R.string.error_msg_prefix); - emitter.onError(new IOException(prefix + response)); - } - } catch (IOException | JSONException e) { - emitter.onError(e); - } - }) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()); - } - 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"; Log.d(TAG, "Feed URL " + String.format(url, country)); diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcher.java b/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcher.java index b19d7d695..eeebd2ebf 100644 --- a/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcher.java +++ b/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcher.java @@ -7,4 +7,10 @@ import java.util.List; public interface PodcastSearcher { Single<List<PodcastSearchResult>> search(String query); + + Single<String> lookupUrl(String resultUrl); + + boolean urlNeedsLookup(String resultUrl); + + String getName(); } diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcherRegistry.java b/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcherRegistry.java new file mode 100644 index 000000000..3f738424b --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearcherRegistry.java @@ -0,0 +1,55 @@ +package de.danoeh.antennapod.discovery; + + +import io.reactivex.Single; + +import java.util.ArrayList; +import java.util.List; + +public class PodcastSearcherRegistry { + private static List<SearcherInfo> searchProviders; + + private PodcastSearcherRegistry() { + } + + public static List<SearcherInfo> getSearchProviders() { + if (searchProviders == null) { + searchProviders = new ArrayList<>(); + searchProviders.add(new SearcherInfo(new CombinedSearcher(), 1.f)); + searchProviders.add(new SearcherInfo(new ItunesPodcastSearcher(), 1.f)); + searchProviders.add(new SearcherInfo(new FyydPodcastSearcher(), 1.f)); + searchProviders.add(new SearcherInfo(new GpodnetPodcastSearcher(), 0.0f)); + } + return searchProviders; + } + + public static Single<String> lookupUrl(String url) { + for (PodcastSearcherRegistry.SearcherInfo searchProviderInfo : getSearchProviders()) { + if (searchProviderInfo.searcher.getClass() != CombinedSearcher.class + && searchProviderInfo.searcher.urlNeedsLookup(url)) { + return searchProviderInfo.searcher.lookupUrl(url); + } + } + return Single.just(url); + } + + public static boolean urlNeedsLookup(String url) { + for (PodcastSearcherRegistry.SearcherInfo searchProviderInfo : getSearchProviders()) { + if (searchProviderInfo.searcher.getClass() != CombinedSearcher.class + && searchProviderInfo.searcher.urlNeedsLookup(url)) { + return true; + } + } + return false; + } + + public static class SearcherInfo { + public final PodcastSearcher searcher; + public final float weight; + + public SearcherInfo(PodcastSearcher searcher, float weight) { + this.searcher = searcher; + this.weight = weight; + } + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java index 2d2244a85..046a39355 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java @@ -19,6 +19,9 @@ import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.activity.OnlineFeedViewActivity; import de.danoeh.antennapod.activity.OpmlImportActivity; +import de.danoeh.antennapod.discovery.CombinedSearcher; +import de.danoeh.antennapod.discovery.FyydPodcastSearcher; +import de.danoeh.antennapod.discovery.ItunesPodcastSearcher; import de.danoeh.antennapod.fragment.gpodnet.GpodnetMainFragment; /** @@ -40,9 +43,9 @@ public class AddFeedFragment extends Fragment { ((AppCompatActivity) getActivity()).setSupportActionBar(root.findViewById(R.id.toolbar)); root.findViewById(R.id.btn_search_itunes).setOnClickListener(v - -> activity.loadChildFragment(new ItunesSearchFragment())); + -> activity.loadChildFragment(OnlineSearchFragment.newInstance(ItunesPodcastSearcher.class))); root.findViewById(R.id.btn_search_fyyd).setOnClickListener(v - -> activity.loadChildFragment(new FyydSearchFragment())); + -> activity.loadChildFragment(OnlineSearchFragment.newInstance(FyydPodcastSearcher.class))); root.findViewById(R.id.btn_search_gpodder).setOnClickListener(v -> activity.loadChildFragment(new GpodnetMainFragment())); @@ -89,7 +92,6 @@ public class AddFeedFragment extends Fragment { private void addUrl(String url) { Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class); intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, url); - intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, getString(R.string.add_feed_label)); startActivity(intent); } @@ -100,12 +102,7 @@ public class AddFeedFragment extends Fragment { addUrl(query); return; } - - Bundle bundle = new Bundle(); - bundle.putString(CombinedSearchFragment.ARGUMENT_QUERY, query); - CombinedSearchFragment fragment = new CombinedSearchFragment(); - fragment.setArguments(bundle); - activity.loadChildFragment(fragment); + activity.loadChildFragment(OnlineSearchFragment.newInstance(CombinedSearcher.class, query)); } @Override diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DiscoveryFragment.java index 256438c2b..89d3c7af9 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/DiscoveryFragment.java @@ -2,39 +2,36 @@ package de.danoeh.antennapod.fragment; import android.content.Intent; import android.os.Bundle; -import androidx.appcompat.app.AppCompatActivity; -import androidx.fragment.app.Fragment; -import androidx.core.view.MenuItemCompat; -import androidx.appcompat.widget.SearchView; import android.util.Log; import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.GridView; import android.widget.ProgressBar; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; +import androidx.fragment.app.Fragment; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.OnlineFeedViewActivity; import de.danoeh.antennapod.adapter.itunes.ItunesAdapter; -import de.danoeh.antennapod.discovery.CombinedSearcher; +import de.danoeh.antennapod.discovery.ItunesTopListLoader; import de.danoeh.antennapod.discovery.PodcastSearchResult; -import de.danoeh.antennapod.menuhandler.MenuItemUtils; import io.reactivex.disposables.Disposable; import java.util.ArrayList; import java.util.List; -public class CombinedSearchFragment extends Fragment { +/** + * Searches iTunes store for top podcasts and displays results in a list. + */ +public class DiscoveryFragment extends Fragment { - private static final String TAG = "CombinedSearchFragment"; - public static final String ARGUMENT_QUERY = "query"; + private static final String TAG = "ItunesSearchFragment"; /** - * Adapter responsible with the search results + * Adapter responsible with the search results. */ private ItunesAdapter adapter; private GridView gridView; @@ -44,15 +41,33 @@ public class CombinedSearchFragment extends Fragment { private TextView txtvEmpty; /** - * List of podcasts retreived from the search + * List of podcasts retreived from the search. */ - private List<PodcastSearchResult> searchResults = new ArrayList<>(); + private List<PodcastSearchResult> searchResults; + private List<PodcastSearchResult> topList; private Disposable disposable; /** - * Constructor + * Replace adapter data with provided search results from SearchTask. + * @param result List of Podcast objects containing search results */ - public CombinedSearchFragment() { + private void updateData(List<PodcastSearchResult> result) { + this.searchResults = result; + adapter.clear(); + if (result != null && result.size() > 0) { + gridView.setVisibility(View.VISIBLE); + txtvEmpty.setVisibility(View.GONE); + for (PodcastSearchResult p : result) { + adapter.add(p); + } + adapter.notifyDataSetInvalidated(); + } else { + gridView.setVisibility(View.GONE); + txtvEmpty.setVisibility(View.VISIBLE); + } + } + + public DiscoveryFragment() { // Required empty public constructor } @@ -63,7 +78,7 @@ public class CombinedSearchFragment extends Fragment { } @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment View root = inflater.inflate(R.layout.fragment_itunes_search, container, false); @@ -75,9 +90,11 @@ public class CombinedSearchFragment extends Fragment { //Show information about the podcast when the list item is clicked gridView.setOnItemClickListener((parent, view1, position, id) -> { PodcastSearchResult podcast = searchResults.get(position); + if (podcast.feedUrl == null) { + return; + } Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class); intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, podcast.feedUrl); - intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, podcast.title); startActivity(intent); }); progressBar = root.findViewById(R.id.progressBar); @@ -85,6 +102,8 @@ public class CombinedSearchFragment extends Fragment { butRetry = root.findViewById(R.id.butRetry); txtvEmpty = root.findViewById(android.R.id.empty); + loadToplist(); + return root; } @@ -97,78 +116,28 @@ public class CombinedSearchFragment extends Fragment { adapter = null; } - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); - inflater.inflate(R.menu.itunes_search, menu); - MenuItem searchItem = menu.findItem(R.id.action_search); - final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem); - sv.setQueryHint(getString(R.string.search_label)); - sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() { - @Override - public boolean onQueryTextSubmit(String s) { - sv.clearFocus(); - search(s); - return true; - } - - @Override - public boolean onQueryTextChange(String s) { - return false; - } - }); - MenuItemCompat.setOnActionExpandListener(searchItem, new MenuItemCompat.OnActionExpandListener() { - @Override - public boolean onMenuItemActionExpand(MenuItem item) { - return true; - } - - @Override - public boolean onMenuItemActionCollapse(MenuItem item) { - getActivity().getSupportFragmentManager().popBackStack(); - return true; - } - }); - MenuItemCompat.expandActionView(searchItem); - - if (getArguments() != null && getArguments().getString(ARGUMENT_QUERY, null) != null) { - sv.setQuery(getArguments().getString(ARGUMENT_QUERY, null), true); - } - } - - private void search(String query) { + private void loadToplist() { if (disposable != null) { disposable.dispose(); } + gridView.setVisibility(View.GONE); + txtvError.setVisibility(View.GONE); + butRetry.setVisibility(View.GONE); + txtvEmpty.setVisibility(View.GONE); + progressBar.setVisibility(View.VISIBLE); - showOnlyProgressBar(); - - CombinedSearcher searcher = new CombinedSearcher(getContext()); - disposable = searcher.search(query).subscribe(result -> { - searchResults = result; - progressBar.setVisibility(View.GONE); - - adapter.clear(); - adapter.addAll(searchResults); - adapter.notifyDataSetInvalidated(); - gridView.setVisibility(!searchResults.isEmpty() ? View.VISIBLE : View.GONE); - txtvEmpty.setVisibility(searchResults.isEmpty() ? View.VISIBLE : View.GONE); - - }, error -> { + ItunesTopListLoader loader = new ItunesTopListLoader(getContext()); + disposable = loader.loadToplist(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.toString()); txtvError.setVisibility(View.VISIBLE); - butRetry.setOnClickListener(v -> search(query)); + butRetry.setOnClickListener(v -> loadToplist()); butRetry.setVisibility(View.VISIBLE); }); } - - private void showOnlyProgressBar() { - gridView.setVisibility(View.GONE); - txtvError.setVisibility(View.GONE); - butRetry.setVisibility(View.GONE); - txtvEmpty.setVisibility(View.GONE); - progressBar.setVisibility(View.VISIBLE); - } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java deleted file mode 100644 index 0e3caf2fe..000000000 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java +++ /dev/null @@ -1,236 +0,0 @@ -package de.danoeh.antennapod.fragment; - -import android.content.Intent; -import android.os.Bundle; -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; -import androidx.fragment.app.Fragment; -import androidx.core.view.MenuItemCompat; -import androidx.appcompat.widget.SearchView; -import androidx.annotation.NonNull; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.GridView; -import android.widget.ProgressBar; -import android.widget.TextView; - -import de.danoeh.antennapod.discovery.ItunesPodcastSearcher; -import de.danoeh.antennapod.discovery.ItunesTopListLoader; -import de.danoeh.antennapod.discovery.PodcastSearchResult; - -import java.util.ArrayList; -import java.util.List; - -import de.danoeh.antennapod.R; -import de.danoeh.antennapod.activity.OnlineFeedViewActivity; -import de.danoeh.antennapod.adapter.itunes.ItunesAdapter; -import de.danoeh.antennapod.menuhandler.MenuItemUtils; -import io.reactivex.disposables.Disposable; - -//Searches iTunes store for given string and displays results in a list -public class ItunesSearchFragment extends Fragment { - - private static final String TAG = "ItunesSearchFragment"; - - - /** - * Adapter responsible with the search results - */ - private ItunesAdapter adapter; - private GridView gridView; - private ProgressBar progressBar; - private TextView txtvError; - private Button butRetry; - private TextView txtvEmpty; - - /** - * List of podcasts retreived from the search - */ - private List<PodcastSearchResult> searchResults; - private List<PodcastSearchResult> topList; - private Disposable disposable; - - /** - * Replace adapter data with provided search results from SearchTask. - * @param result List of Podcast objects containing search results - */ - private void updateData(List<PodcastSearchResult> result) { - this.searchResults = result; - adapter.clear(); - if (result != null && result.size() > 0) { - gridView.setVisibility(View.VISIBLE); - txtvEmpty.setVisibility(View.GONE); - for (PodcastSearchResult p : result) { - adapter.add(p); - } - adapter.notifyDataSetInvalidated(); - } else { - gridView.setVisibility(View.GONE); - txtvEmpty.setVisibility(View.VISIBLE); - } - } - - /** - * Constructor - */ - public ItunesSearchFragment() { - // Required empty public constructor - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setHasOptionsMenu(true); - } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - // Inflate the layout for this fragment - View root = inflater.inflate(R.layout.fragment_itunes_search, container, false); - ((AppCompatActivity) getActivity()).setSupportActionBar(root.findViewById(R.id.toolbar)); - gridView = root.findViewById(R.id.gridView); - adapter = new ItunesAdapter(getActivity(), new ArrayList<>()); - gridView.setAdapter(adapter); - - //Show information about the podcast when the list item is clicked - gridView.setOnItemClickListener((parent, view1, position, id) -> { - PodcastSearchResult podcast = searchResults.get(position); - if (podcast.feedUrl == null) { - return; - } - gridView.setVisibility(View.GONE); - progressBar.setVisibility(View.VISIBLE); - ItunesTopListLoader loader = new ItunesTopListLoader(getContext()); - disposable = loader.getFeedUrl(podcast) - .subscribe(feedUrl -> { - progressBar.setVisibility(View.GONE); - gridView.setVisibility(View.VISIBLE); - Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class); - intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, feedUrl); - intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, "iTunes"); - startActivity(intent); - }, error -> { - Log.e(TAG, Log.getStackTraceString(error)); - progressBar.setVisibility(View.GONE); - gridView.setVisibility(View.VISIBLE); - String prefix = getString(R.string.error_msg_prefix); - new AlertDialog.Builder(getActivity()) - .setMessage(prefix + " " + error.getMessage()) - .setPositiveButton(android.R.string.ok, null) - .show(); - }); - }); - progressBar = root.findViewById(R.id.progressBar); - txtvError = root.findViewById(R.id.txtvError); - butRetry = root.findViewById(R.id.butRetry); - txtvEmpty = root.findViewById(android.R.id.empty); - - loadToplist(); - - return root; - } - - @Override - public void onDestroy() { - super.onDestroy(); - if (disposable != null) { - disposable.dispose(); - } - adapter = null; - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); - inflater.inflate(R.menu.itunes_search, menu); - MenuItem searchItem = menu.findItem(R.id.action_search); - final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem); - sv.setQueryHint(getString(R.string.search_itunes_label)); - sv.setOnQueryTextListener(new androidx.appcompat.widget.SearchView.OnQueryTextListener() { - @Override - public boolean onQueryTextSubmit(String s) { - sv.clearFocus(); - search(s); - return true; - } - - @Override - public boolean onQueryTextChange(String s) { - return false; - } - }); - MenuItemCompat.setOnActionExpandListener(searchItem, new MenuItemCompat.OnActionExpandListener() { - @Override - public boolean onMenuItemActionExpand(MenuItem item) { - return true; - } - - @Override - public boolean onMenuItemActionCollapse(MenuItem item) { - if (searchResults != null) { - searchResults = null; - updateData(topList); - } - return true; - } - }); - } - - private void loadToplist() { - if (disposable != null) { - disposable.dispose(); - } - gridView.setVisibility(View.GONE); - txtvError.setVisibility(View.GONE); - butRetry.setVisibility(View.GONE); - txtvEmpty.setVisibility(View.GONE); - progressBar.setVisibility(View.VISIBLE); - - ItunesTopListLoader loader = new ItunesTopListLoader(getContext()); - disposable = loader.loadToplist(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.toString()); - txtvError.setVisibility(View.VISIBLE); - butRetry.setOnClickListener(v -> loadToplist()); - butRetry.setVisibility(View.VISIBLE); - }); - } - - private void search(String query) { - if (disposable != null) { - disposable.dispose(); - } - gridView.setVisibility(View.GONE); - txtvError.setVisibility(View.GONE); - butRetry.setVisibility(View.GONE); - txtvEmpty.setVisibility(View.GONE); - progressBar.setVisibility(View.VISIBLE); - - ItunesPodcastSearcher searcher = new ItunesPodcastSearcher(getContext()); - disposable = searcher.search(query).subscribe(podcasts -> { - progressBar.setVisibility(View.GONE); - updateData(podcasts); - }, error -> { - Log.e(TAG, Log.getStackTraceString(error)); - progressBar.setVisibility(View.GONE); - txtvError.setText(error.toString()); - txtvError.setVisibility(View.VISIBLE); - butRetry.setOnClickListener(v -> search(query)); - butRetry.setVisibility(View.VISIBLE); - }); - } - -} diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/OnlineSearchFragment.java index 547bd6b24..d9c31f993 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/OnlineSearchFragment.java @@ -20,22 +20,25 @@ import android.widget.TextView; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.OnlineFeedViewActivity; import de.danoeh.antennapod.adapter.itunes.ItunesAdapter; -import de.danoeh.antennapod.discovery.FyydPodcastSearcher; import de.danoeh.antennapod.discovery.PodcastSearchResult; -import de.danoeh.antennapod.menuhandler.MenuItemUtils; +import de.danoeh.antennapod.discovery.PodcastSearcher; +import de.danoeh.antennapod.discovery.PodcastSearcherRegistry; import io.reactivex.disposables.Disposable; import java.util.ArrayList; import java.util.List; -public class FyydSearchFragment extends Fragment { +public class OnlineSearchFragment extends Fragment { private static final String TAG = "FyydSearchFragment"; + private static final String ARG_SEARCHER = "searcher"; + private static final String ARG_QUERY = "query"; /** * Adapter responsible with the search results */ private ItunesAdapter adapter; + private PodcastSearcher searchProvider; private GridView gridView; private ProgressBar progressBar; private TextView txtvError; @@ -48,10 +51,23 @@ public class FyydSearchFragment extends Fragment { private List<PodcastSearchResult> searchResults; private Disposable disposable; + public static OnlineSearchFragment newInstance(Class<? extends PodcastSearcher> searchProvider) { + return newInstance(searchProvider, null); + } + + public static OnlineSearchFragment newInstance(Class<? extends PodcastSearcher> searchProvider, String query) { + OnlineSearchFragment fragment = new OnlineSearchFragment(); + Bundle arguments = new Bundle(); + arguments.putString(ARG_SEARCHER, searchProvider.getName()); + arguments.putString(ARG_QUERY, query); + fragment.setArguments(arguments); + return fragment; + } + /** * Constructor */ - public FyydSearchFragment() { + public OnlineSearchFragment() { // Required empty public constructor } @@ -59,11 +75,20 @@ public class FyydSearchFragment extends Fragment { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); + + for (PodcastSearcherRegistry.SearcherInfo info : PodcastSearcherRegistry.getSearchProviders()) { + if (info.searcher.getClass().getName().equals(getArguments().getString(ARG_SEARCHER))) { + searchProvider = info.searcher; + break; + } + } + if (searchProvider == null) { + throw new IllegalArgumentException("Podcast searcher not found"); + } } @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment View root = inflater.inflate(R.layout.fragment_itunes_search, container, false); ((AppCompatActivity) getActivity()).setSupportActionBar(root.findViewById(R.id.toolbar)); @@ -76,7 +101,6 @@ public class FyydSearchFragment extends Fragment { PodcastSearchResult podcast = searchResults.get(position); Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class); intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, podcast.feedUrl); - intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, podcast.title); startActivity(intent); }); progressBar = root.findViewById(R.id.progressBar); @@ -84,6 +108,7 @@ public class FyydSearchFragment extends Fragment { butRetry = root.findViewById(R.id.butRetry); txtvEmpty = root.findViewById(android.R.id.empty); + txtvEmpty.setText(getString(R.string.search_powered_by, searchProvider.getName())); return root; } @@ -99,10 +124,10 @@ public class FyydSearchFragment extends Fragment { @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); - inflater.inflate(R.menu.itunes_search, menu); + inflater.inflate(R.menu.online_search, menu); MenuItem searchItem = menu.findItem(R.id.action_search); final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem); - sv.setQueryHint(getString(R.string.search_fyyd_label)); + sv.setQueryHint(getString(R.string.search_podcast_hint)); sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String s) { @@ -116,7 +141,7 @@ public class FyydSearchFragment extends Fragment { return false; } }); - MenuItemCompat.setOnActionExpandListener(searchItem, new MenuItemCompat.OnActionExpandListener() { + searchItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() { @Override public boolean onMenuItemActionExpand(MenuItem item) { return true; @@ -128,7 +153,12 @@ public class FyydSearchFragment extends Fragment { return true; } }); - MenuItemCompat.expandActionView(searchItem); + searchItem.expandActionView(); + + if (getArguments().getString(ARG_QUERY, null) != null) { + sv.setQuery(getArguments().getString(ARG_QUERY, null), true); + } + } private void search(String query) { @@ -136,26 +166,23 @@ public class FyydSearchFragment extends Fragment { disposable.dispose(); } showOnlyProgressBar(); - - FyydPodcastSearcher searcher = new FyydPodcastSearcher(); - disposable = searcher.search(query).subscribe(result -> { + disposable = searchProvider.search(query).subscribe(result -> { searchResults = result; progressBar.setVisibility(View.GONE); - adapter.clear(); adapter.addAll(searchResults); adapter.notifyDataSetInvalidated(); gridView.setVisibility(!searchResults.isEmpty() ? View.VISIBLE : View.GONE); txtvEmpty.setVisibility(searchResults.isEmpty() ? View.VISIBLE : View.GONE); - + txtvEmpty.setText(getString(R.string.no_results_for_query, query)); }, error -> { - Log.e(TAG, Log.getStackTraceString(error)); - progressBar.setVisibility(View.GONE); - txtvError.setText(error.toString()); - txtvError.setVisibility(View.VISIBLE); - butRetry.setOnClickListener(v -> search(query)); - butRetry.setVisibility(View.VISIBLE); - }); + Log.e(TAG, Log.getStackTraceString(error)); + progressBar.setVisibility(View.GONE); + txtvError.setText(error.toString()); + txtvError.setVisibility(View.VISIBLE); + butRetry.setOnClickListener(v -> search(query)); + butRetry.setVisibility(View.VISIBLE); + }); } private void showOnlyProgressBar() { 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 d8d0f41c5..d6bcdd79c 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java @@ -3,7 +3,6 @@ package de.danoeh.antennapod.fragment; import android.content.Intent; import android.os.Bundle; import android.util.DisplayMetrics; -import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; import android.util.Log; import android.view.LayoutInflater; @@ -41,7 +40,7 @@ public class QuickFeedDiscoveryFragment extends Fragment implements AdapterView. View root = inflater.inflate(R.layout.quick_feed_discovery, container, false); View discoverMore = root.findViewById(R.id.discover_more); discoverMore.setOnClickListener(v -> - ((MainActivity) getActivity()).loadChildFragment(new ItunesSearchFragment())); + ((MainActivity) getActivity()).loadChildFragment(new DiscoveryFragment())); discoverGridLayout = root.findViewById(R.id.discover_grid); progressBar = root.findViewById(R.id.discover_progress_bar); @@ -106,23 +105,8 @@ public class QuickFeedDiscoveryFragment extends Fragment implements AdapterView. if (podcast.feedUrl == null) { return; } - view.setAlpha(0.5f); - ItunesTopListLoader loader = new ItunesTopListLoader(getContext()); - disposable = loader.getFeedUrl(podcast) - .subscribe(feedUrl -> { - view.setAlpha(1f); - Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class); - intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, feedUrl); - intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, getString(R.string.add_feed_label)); - startActivity(intent); - }, error -> { - Log.e(TAG, Log.getStackTraceString(error)); - view.setAlpha(1f); - String prefix = getString(R.string.error_msg_prefix); - new AlertDialog.Builder(getActivity()) - .setMessage(prefix + " " + error.getMessage()) - .setPositiveButton(android.R.string.ok, null) - .show(); - }); + Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class); + intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, podcast.feedUrl); + startActivity(intent); } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java index 87378ae6c..e1c85a2d3 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java @@ -104,7 +104,6 @@ public abstract class PodcastListFragment extends Fragment { Log.d(TAG, "Selected podcast: " + selection.toString()); Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class); intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, selection.getUrl()); - intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, getString(R.string.gpodnet_main_label)); startActivity(intent); } |