From 2ef464ad93525e93e74f1a8f298059acc6977d8d Mon Sep 17 00:00:00 2001 From: Tony Tam <149837+tonytamsf@users.noreply.github.com> Date: Sat, 17 Oct 2020 13:50:08 -0700 Subject: Discovery filter by country & hide discovery on first subscribe screen (#4515) --- .../antennapod/discovery/ItunesTopListLoader.java | 44 +++++++-- .../antennapod/fragment/DiscoveryFragment.java | 110 ++++++++++++++++++--- .../antennapod/fragment/OnlineSearchFragment.java | 3 + .../fragment/QuickFeedDiscoveryFragment.java | 71 ++++++++++--- app/src/main/res/layout/fragment_itunes_search.xml | 33 +++++-- 5 files changed, 218 insertions(+), 43 deletions(-) (limited to 'app') 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 e93a89ef0..e1034f89b 100644 --- a/app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java +++ b/app/src/main/java/de/danoeh/antennapod/discovery/ItunesTopListLoader.java @@ -1,6 +1,7 @@ package de.danoeh.antennapod.discovery; import android.content.Context; +import android.content.SharedPreferences; import android.util.Log; import de.danoeh.antennapod.R; @@ -23,24 +24,46 @@ import java.util.List; import java.util.Locale; import java.util.concurrent.TimeUnit; +import static android.content.Context.MODE_PRIVATE; + public class ItunesTopListLoader { private static final String TAG = "ITunesTopListLoader"; private final Context context; + public static final String PREF_KEY_COUNTRY_CODE = "country_code"; + public static final String PREFS = "CountryRegionPrefs"; + public static final String DISCOVER_HIDE_FAKE_COUNTRY_CODE = "00"; + public static final String COUNTRY_CODE_UNSET = "99"; public ItunesTopListLoader(Context context) { this.context = context; } - public Single> loadToplist(int limit) { + public Single> loadToplist() { + String defaultCountry = Locale.getDefault().getCountry(); + SharedPreferences prefs = context.getSharedPreferences(PREFS, MODE_PRIVATE); + String countryCode = prefs.getString(PREF_KEY_COUNTRY_CODE, COUNTRY_CODE_UNSET); + return this.loadToplist(countryCode, 25); + } + + public Single> loadToplist(String country, int limit) { return Single.create((SingleOnSubscribe>) emitter -> { - String country = Locale.getDefault().getCountry(); OkHttpClient client = AntennapodHttpClient.getHttpClient(); String feedString; + String loadCountry = country; + if (COUNTRY_CODE_UNSET.equals(country)) { + loadCountry = Locale.getDefault().getCountry(); + } try { - feedString = getTopListFeed(client, country, limit); + feedString = getTopListFeed(client, loadCountry, limit); } catch (IOException e) { - feedString = getTopListFeed(client, "us", limit); + if (COUNTRY_CODE_UNSET.equals(country)) { + feedString = getTopListFeed(client, "US", limit); + } else { + emitter.onError(e); + return; + } } + List podcasts = parseFeed(feedString); emitter.onSuccess(podcasts); }) @@ -59,6 +82,9 @@ public class ItunesTopListLoader { if (response.isSuccessful()) { return response.body().string(); } + if (response.code() == 400) { + throw new IOException("iTunes does not have data for the selected country."); + } String prefix = context.getString(R.string.error_msg_prefix); throw new IOException(prefix + response); } @@ -66,8 +92,14 @@ public class ItunesTopListLoader { private List parseFeed(String jsonString) throws JSONException { JSONObject result = new JSONObject(jsonString); - JSONObject feed = result.getJSONObject("feed"); - JSONArray entries = feed.getJSONArray("entry"); + JSONObject feed; + JSONArray entries; + try { + feed = result.getJSONObject("feed"); + entries = feed.getJSONArray("entry"); + } catch (JSONException e) { + return new ArrayList<>(); + } List results = new ArrayList<>(); for (int i = 0; i < entries.length(); i++) { 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 89d3c7af9..5bc950d50 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/DiscoveryFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/DiscoveryFragment.java @@ -1,27 +1,41 @@ package de.danoeh.antennapod.fragment; import android.content.Intent; +import android.content.SharedPreferences; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.GridView; import android.widget.ProgressBar; +import android.widget.Spinner; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; + +import org.greenrobot.eventbus.EventBus; + import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.OnlineFeedViewActivity; import de.danoeh.antennapod.adapter.itunes.ItunesAdapter; +import de.danoeh.antennapod.core.event.DiscoveryDefaultUpdateEvent; import de.danoeh.antennapod.discovery.ItunesTopListLoader; import de.danoeh.antennapod.discovery.PodcastSearchResult; import io.reactivex.disposables.Disposable; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Locale; + +import static android.content.Context.MODE_PRIVATE; /** * Searches iTunes store for top podcasts and displays results in a list. @@ -29,6 +43,7 @@ import java.util.List; public class DiscoveryFragment extends Fragment { private static final String TAG = "ItunesSearchFragment"; + private SharedPreferences prefs; /** * Adapter responsible with the search results. @@ -46,6 +61,7 @@ public class DiscoveryFragment extends Fragment { private List searchResults; private List topList; private Disposable disposable; + private String countryCode = "US"; /** * Replace adapter data with provided search results from SearchTask. @@ -75,6 +91,8 @@ public class DiscoveryFragment extends Fragment { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); + prefs = getActivity().getSharedPreferences(ItunesTopListLoader.PREFS, MODE_PRIVATE); + countryCode = prefs.getString(ItunesTopListLoader.PREF_KEY_COUNTRY_CODE, Locale.getDefault().getCountry()); } @Override @@ -97,13 +115,64 @@ public class DiscoveryFragment extends Fragment { intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, podcast.feedUrl); startActivity(intent); }); + + List countryCodeArray = new ArrayList(Arrays.asList(Locale.getISOCountries())); + HashMap countryCodeNames = new HashMap(); + for (String code: countryCodeArray) { + Locale locale = new Locale("", code); + String countryName = locale.getDisplayCountry(); + if (countryName != null) { + countryCodeNames.put(code, countryName); + } + } + + List countryNamesSort = new ArrayList(countryCodeNames.values()); + Collections.sort(countryNamesSort); + countryNamesSort.add(0, getResources().getString(R.string.discover_hide)); + + Spinner countrySpinner = root.findViewById(R.id.spinner_country); + ArrayAdapter dataAdapter = new ArrayAdapter(this.getContext(), + android.R.layout.simple_spinner_item, + countryNamesSort); + dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + countrySpinner.setAdapter(dataAdapter); + int pos = countryNamesSort.indexOf(countryCodeNames.get(countryCode)); + countrySpinner.setSelection(pos); + + countrySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView countrySpinner, View view, int position, long id) { + String countryName = (String) countrySpinner.getItemAtPosition(position); + + if (countryName.equals(getResources().getString(R.string.discover_hide))) { + countryCode = ItunesTopListLoader.DISCOVER_HIDE_FAKE_COUNTRY_CODE; + } else { + for (Object o : countryCodeNames.keySet()) { + if (countryCodeNames.get(o).equals(countryName)) { + countryCode = o.toString(); + break; + } + } + } + + prefs.edit() + .putString(ItunesTopListLoader.PREF_KEY_COUNTRY_CODE, countryCode) + .apply(); + + EventBus.getDefault().post(new DiscoveryDefaultUpdateEvent()); + loadToplist(countryCode); + } + + @Override + public void onNothingSelected(AdapterView parent) { + } + }); 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(); - + loadToplist(countryCode); return root; } @@ -116,28 +185,39 @@ public class DiscoveryFragment extends Fragment { adapter = null; } - private void loadToplist() { + private void loadToplist(String country) { 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 -> { + if (country.equals(ItunesTopListLoader.DISCOVER_HIDE_FAKE_COUNTRY_CODE)) { + gridView.setVisibility(View.GONE); + txtvError.setVisibility(View.VISIBLE); + txtvError.setText(getResources().getString(R.string.discover_is_hidden)); + butRetry.setVisibility(View.GONE); + txtvEmpty.setVisibility(View.GONE); 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); - }); + } else { + 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); + }); + } } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/OnlineSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/OnlineSearchFragment.java index 037c687a1..435590a0c 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/OnlineSearchFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/OnlineSearchFragment.java @@ -29,6 +29,8 @@ import io.reactivex.disposables.Disposable; import java.util.ArrayList; import java.util.List; +import static android.view.View.INVISIBLE; + public class OnlineSearchFragment extends Fragment { private static final String TAG = "FyydSearchFragment"; @@ -93,6 +95,7 @@ public class OnlineSearchFragment extends Fragment { // 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)); + root.findViewById(R.id.spinner_country).setVisibility(INVISIBLE); gridView = root.findViewById(R.id.gridView); adapter = new ItunesAdapter(getActivity(), new ArrayList<>()); gridView.setAdapter(adapter); 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 38c068a3f..c994b4d8b 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java @@ -1,9 +1,11 @@ package de.danoeh.antennapod.fragment; import android.content.Intent; +import android.content.SharedPreferences; import android.os.Bundle; import android.util.DisplayMetrics; import androidx.fragment.app.Fragment; + import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -14,16 +16,25 @@ import android.widget.GridView; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; + +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.event.DiscoveryDefaultUpdateEvent; import de.danoeh.antennapod.discovery.ItunesTopListLoader; import de.danoeh.antennapod.discovery.PodcastSearchResult; import io.reactivex.disposables.Disposable; import java.util.ArrayList; import java.util.List; +import java.util.Locale; + +import static android.content.Context.MODE_PRIVATE; public class QuickFeedDiscoveryFragment extends Fragment implements AdapterView.OnItemClickListener { @@ -36,6 +47,7 @@ public class QuickFeedDiscoveryFragment extends Fragment implements AdapterView. private GridView discoverGridLayout; private TextView errorTextView; private LinearLayout errorView; + private Button errorRetry; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -49,7 +61,7 @@ public class QuickFeedDiscoveryFragment extends Fragment implements AdapterView. progressBar = root.findViewById(R.id.discover_progress_bar); errorView = root.findViewById(R.id.discover_error); errorTextView = root.findViewById(R.id.discover_error_txtV); - Button errorRetry = root.findViewById(R.id.discover_error_retry_btn); + errorRetry = root.findViewById(R.id.discover_error_retry_btn); errorRetry.setOnClickListener((listener) -> loadToplist()); adapter = new FeedDiscoverAdapter((MainActivity) getActivity()); @@ -73,36 +85,67 @@ public class QuickFeedDiscoveryFragment extends Fragment implements AdapterView. adapter.updateData(dummies); loadToplist(); + + EventBus.getDefault().register(this); return root; } @Override public void onDestroy() { super.onDestroy(); + EventBus.getDefault().unregister(this); if (disposable != null) { disposable.dispose(); } } + @Subscribe(threadMode = ThreadMode.MAIN) + @SuppressWarnings("unused") + public void onDiscoveryDefaultUpdateEvent(DiscoveryDefaultUpdateEvent event) { + loadToplist(); + } + private void loadToplist() { progressBar.setVisibility(View.VISIBLE); discoverGridLayout.setVisibility(View.INVISIBLE); errorView.setVisibility(View.GONE); + errorRetry.setVisibility(View.INVISIBLE); ItunesTopListLoader loader = new ItunesTopListLoader(getContext()); - disposable = loader.loadToplist(NUM_SUGGESTIONS) - .subscribe(podcasts -> { - errorView.setVisibility(View.GONE); - progressBar.setVisibility(View.GONE); - discoverGridLayout.setVisibility(View.VISIBLE); - adapter.updateData(podcasts); - }, error -> { - Log.e(TAG, Log.getStackTraceString(error)); - errorTextView.setText(error.getLocalizedMessage()); - errorView.setVisibility(View.VISIBLE); - progressBar.setVisibility(View.GONE); - discoverGridLayout.setVisibility(View.INVISIBLE); - }); + SharedPreferences prefs = getActivity().getSharedPreferences(ItunesTopListLoader.PREFS, MODE_PRIVATE); + String countryCode = prefs.getString(ItunesTopListLoader.PREF_KEY_COUNTRY_CODE, + Locale.getDefault().getCountry()); + if (countryCode.equals(ItunesTopListLoader.DISCOVER_HIDE_FAKE_COUNTRY_CODE)) { + errorTextView.setText(String.format(getResources().getString(R.string.discover_is_hidden), + getResources().getString(R.string.discover_hide))); + errorView.setVisibility(View.VISIBLE); + progressBar.setVisibility(View.GONE); + discoverGridLayout.setVisibility(View.INVISIBLE); + errorRetry.setVisibility(View.INVISIBLE); + return; + } + + disposable = loader.loadToplist(countryCode, NUM_SUGGESTIONS) + .subscribe( + podcasts -> { + errorView.setVisibility(View.GONE); + progressBar.setVisibility(View.GONE); + discoverGridLayout.setVisibility(View.VISIBLE); + if (podcasts.size() == 0) { + errorTextView.setText(getResources().getText(R.string.search_status_no_results)); + errorView.setVisibility(View.VISIBLE); + discoverGridLayout.setVisibility(View.INVISIBLE); + } else { + adapter.updateData(podcasts); + } + }, error -> { + Log.e(TAG, Log.getStackTraceString(error)); + errorTextView.setText(error.getLocalizedMessage()); + errorView.setVisibility(View.VISIBLE); + progressBar.setVisibility(View.GONE); + discoverGridLayout.setVisibility(View.INVISIBLE); + errorRetry.setVisibility(View.VISIBLE); + }); } @Override diff --git a/app/src/main/res/layout/fragment_itunes_search.xml b/app/src/main/res/layout/fragment_itunes_search.xml index d82051867..6e00bdf0d 100644 --- a/app/src/main/res/layout/fragment_itunes_search.xml +++ b/app/src/main/res/layout/fragment_itunes_search.xml @@ -4,18 +4,35 @@ android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto"> - + android:orientation="horizontal" + android:id="@+id/browsing" + android:layout_alignParentTop="true"> + + + +