package de.danoeh.antennapod.fragment;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.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 com.afollestad.materialdialogs.MaterialDialog;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
import de.danoeh.antennapod.adapter.itunes.ItunesAdapter;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
import static de.danoeh.antennapod.adapter.itunes.ItunesAdapter.Podcast;
//Searches iTunes store for given string and displays results in a list
public class ItunesSearchFragment extends Fragment {
private static final String TAG = "ItunesSearchFragment";
private static final String API_URL = "https://itunes.apple.com/search?media=podcast&term=%s";
/**
* 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<Podcast> searchResults;
private List<Podcast> topList;
private Subscription subscription;
/**
* Replace adapter data with provided search results from SearchTask.
* @param result List of Podcast objects containing search results
*/
void updateData(List<Podcast> result) {
this.searchResults = result;
adapter.clear();
if (result != null && result.size() > 0) {
gridView.setVisibility(View.VISIBLE);
txtvEmpty.setVisibility(View.GONE);
for (Podcast 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(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View root = inflater.inflate(R.layout.fragment_itunes_search, container, false);
gridView = (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) -> {
Podcast podcast = searchResults.get(position);
if (!podcast.feedUrl.contains("itunes.apple.com")) {
Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class);
intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, podcast.feedUrl);
intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, "iTunes");
startActivity(intent);
} else {
gridView.setVisibility(View.GONE);
progressBar.setVisibility(View.VISIBLE);
subscription = Observable.create((Observable.OnSubscribe<String>) subscriber -> {
OkHttpClient client = AntennapodHttpClient.getHttpClient();
Request.Builder httpReq = new Request.Builder()
.url(podcast.feedUrl)
.header("User-Agent", ClientConfig.USER_AGENT);
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");
subscriber.onNext(feedUrl);
} else {
String prefix = getString(R.string.error_msg_prefix);
subscriber.onError(new IOException(prefix + response));
}
} catch (IOException | JSONException e) {
subscriber.onError(e);
}
subscriber.onCompleted();
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.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 MaterialDialog.Builder(getActivity())
.content(prefix + " " + error.getMessage())
.neutralText(android.R.string.ok)
.show();
});
}
});
progressBar = (ProgressBar) root.findViewById(R.id.progressBar);
txtvError = (TextView) root.findViewById(R.id.txtvError);
butRetry = (Button) root.findViewById(R.id.butRetry);
txtvEmpty = (TextView) root.findViewById(android.R.id.empty);
loadToplist();
return root;
}
@Override
public void onDestroy() {
super.onDestroy();
if (subscription != null) {
subscription.unsubscribe();
}
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);
MenuItemUtils.adjustTextColor(getActivity(), sv);
sv.setQueryHint(getString(R.string.search_itunes_label));
sv.setOnQueryTextListener(new android.support.v7.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 (subscription != null) {
subscription.unsubscribe();
}
gridView.setVisibility(View.GONE);
txtvError.setVisibility(View.GONE);
butRetry.setVisibility(View.GONE);
txtvEmpty.setVisibility(View.GONE);
progressBar.setVisibility(View.VISIBLE);
subscription = Observable.create((Observable.OnSubscribe<List<Podcast>>) subscriber -> {
String lang = Locale.getDefault().getLanguage();
String url = "https://itunes.apple.com/" + lang + "/rss/toppodcasts/limit=25/explicit=true/json";
OkHttpClient client = AntennapodHttpClient.getHttpClient();
Request.Builder httpReq = new Request.Builder()
.url(url)
.header("User-Agent", ClientConfig.USER_AGENT);
List<Podcast> results = new ArrayList<>();
try {
Response response = client.newCall(httpReq.build()).execute();
if(!response.isSuccessful()) {
// toplist for language does not exist, fall back to united states
url = "https://itunes.apple.com/us/rss/toppodcasts/limit=25/explicit=true/json";
httpReq = new Request.Builder()
.url(url)
.header("User-Agent", ClientConfig.USER_AGENT);
response = client.newCall(httpReq.build()).execute();
}
if(response.isSuccessful()) {
String resultString = response.body().string();
JSONObject result = new JSONObject(resultString);
JSONObject feed = result.getJSONObject("feed");
JSONArray entries = feed.getJSONArray("entry");
for(int i=0; i < entries.length(); i++) {
JSONObject json = entries.getJSONObject(i);
Podcast podcast = Podcast.fromToplist(json);
results.add(podcast);
}
}
else {
String prefix = getString(R.string.error_msg_prefix);
subscriber.onError(new IOException(prefix + response));
}
} catch (IOException | JSONException e) {
subscriber.onError(e);
}
subscriber.onNext(results);
subscriber.onCompleted();
})
.subscribeOn(Schedulers.newThread())
.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.toString());
txtvError.setVisibility(View.VISIBLE);
butRetry.setOnClickListener(v -> loadToplist());
butRetry.setVisibility(View.VISIBLE);
});
}
private void search(String query) {
if (subscription != null) {
subscription.unsubscribe();
}
gridView.setVisibility(View.GONE);
txtvError.setVisibility(View.GONE);
butRetry.setVisibility(View.GONE);
txtvEmpty.setVisibility(View.GONE);
progressBar.setVisibility(View.VISIBLE);
subscription = rx.Observable.create((Observable.OnSubscribe<List<Podcast>>) subscriber -> {
String encodedQuery = null;
try {
encodedQuery = URLEncoder.encode(query, "UTF-8");
} catch (UnsupportedEncodingException e) {
// this won't ever be thrown
}
if (encodedQuery == null) {
encodedQuery = query; // failsafe
}
//Spaces in the query need to be replaced with '+' character.
String formattedUrl = String.format(API_URL, query).replace(' ', '+');
OkHttpClient client = AntennapodHttpClient.getHttpClient();
Request.Builder httpReq = new Request.Builder()
.url(formattedUrl)
.header("User-Agent", ClientConfig.USER_AGENT);
List<Podcast> podcasts = new ArrayList<>();
try {
Response response = client.newCall(httpReq.build()).execute();
if(response.isSuccessful()) {
String resultString = response.body().string();
JSONObject result = new JSONObject(resultString);
JSONArray j = result.getJSONArray("results");
for (int i = 0; i < j.length(); i++) {
JSONObject podcastJson = j.getJSONObject(i);
Podcast podcast = Podcast.fromSearch(podcastJson);
podcasts.add(podcast);
}
}
else {
String prefix = getString(R.string.error_msg_prefix);
subscriber.onError(new IOException(prefix + response));
}
} catch (IOException | JSONException e) {
subscriber.onError(e);
}
subscriber.onNext(podcasts);
subscriber.onCompleted();
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.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);
});
}
}