package com.airlocksoftware.hackernews.loader; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.ArrayList; import java.util.List; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.StatusLine; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.json.JSONArray; import org.json.JSONObject; import android.content.Context; import android.support.v4.content.AsyncTaskLoader; import android.util.Log; import com.airlocksoftware.hackernews.application.MainApplication; import com.airlocksoftware.hackernews.data.UserPrefs; import com.airlocksoftware.hackernews.activity.SearchActivity.SearchType; import com.airlocksoftware.hackernews.activity.SearchActivity.SortType; import com.airlocksoftware.hackernews.loader.SearchLoader.SearchResult; import com.airlocksoftware.hackernews.model.Request; import com.airlocksoftware.hackernews.model.Result; import com.airlocksoftware.hackernews.model.SearchItem; import com.google.gson.Gson; public class SearchLoader extends AsyncTaskLoader<SearchResult> { Request mRequest; String mQuery; SortType mSort; SearchType mType; /** * Which page to request search results for. Use 0 for a new search, and other positive integers as offsets from * 0. **/ int mPage = 0; // Constants @SuppressWarnings("unused") private static final String TAG = SearchLoader.class.getSimpleName(); private static final String START_QUERY = "https://hn.algolia.io/api/v1/"; private static final String WEIGHTS = "&weights[title]=1.1&weights[text]=0.7&weights[domain]=2.0&weights[username]=0.1&weights[type]=0.0&boosts[fields][points]=0.15&boosts[fields][num_comments]=0.15&boosts[functions][pow(2,div(div(ms(create_ts,NOW),3600000),72))]=200.0"; public static class SearchResult { public SearchResult(Result result) { this.result = result; } public SearchResult() { // default empty constructor } public Result result; public List<SearchItem> items; public int hits = -1; public int page = 0; } public SearchLoader(Context context, Request request, String query, SortType sort, SearchType type, int start) { super(context); mRequest = request; mQuery = query; mSort = sort; mType = type; mPage = start; } @Override public SearchResult loadInBackground() { if (mQuery == null || mSort == null || mType == null || mRequest == Request.EMPTY) { return new SearchResult(Result.EMPTY); } String response = performSearch(); int hits = -1; List<SearchItem> items = new ArrayList<SearchItem>(); SearchResult search = new SearchResult(); try { Gson gson = new Gson(); JSONObject obj = new JSONObject(response); hits = obj.getInt("nbHits"); JSONArray results = obj.getJSONArray("hits"); for (int i = 0; i < results.length(); i++) { SearchItem searchItem = gson.fromJson( results.getJSONObject(i).toString(), SearchItem.class ); items.add(searchItem); } } catch (Exception e) { search.result = Result.FAILURE; e.printStackTrace(); } search.items = items; search.hits = hits; search.page = mPage; if (search.result == null) search.result = Result.SUCCESS; // search.result = (items != null && hits != -1) ? Result.SUCCESS : Result.EMPTY; return search; } /** Builds a search url from instance variables, then performs a get of the url. Returns the result as a string. **/ private String performSearch() { String searchUrl = buildSearchUrl(); StringBuilder builder = new StringBuilder(); HttpClient client = new DefaultHttpClient(); HttpGet httpGet = new HttpGet(searchUrl); // // UserPrefs prefs = new UserPrefs(MainApplication.getInstance().getApplicationContext()); // // if (prefs.getCompressData()) { // httpGet.addHeader("Accept-Encoding", "gzip"); // } try { HttpResponse response = client.execute(httpGet); StatusLine statusLine = response.getStatusLine(); int statusCode = statusLine.getStatusCode(); if (statusCode == 200) { HttpEntity entity = response.getEntity(); InputStream content = entity.getContent(); BufferedReader reader = new BufferedReader(new InputStreamReader(content)); String line; while ((line = reader.readLine()) != null) { builder.append(line); } } else { Log.e(searchUrl, "Failed to download file"); // notify user of search failure } } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } String response = builder.toString(); return response; } private String buildSearchUrl() { StringBuilder builder = new StringBuilder(START_QUERY); String endpoint; String query = ""; try { query = URLEncoder.encode(mQuery, "UTF-8"); } catch (UnsupportedEncodingException e) { // tell user the query was invalid? e.printStackTrace(); } switch (mSort) { case RELEVANCE: endpoint = "search"; break; case DATE: endpoint = "search_by_date"; break; // POINTS no longer a valid sort! // will have to sort ourselves if we want to support this // case POINTS: // endpoint = "search"; // break; default: throw new RuntimeException("Error: didn't receive a valid SortType" + mSort.toString()); } builder.append(endpoint); switch (mType) { case ALL: // don't append filter builder.append("?query=" + query); builder.append("&page=" + mPage); break; case STORIES: builder.append("?query=" + query); builder.append("&page=" + mPage); builder.append("&tags=story"); break; case COMMENTS: builder.append("?query=" + query); builder.append("&page=" + mPage); builder.append("&tags=comment"); break; case USERS: builder.append("?page=" + mPage); builder.append("&tags=author_" + query); break; default: throw new RuntimeException("Error: didn't receive a valid SearchType" + mType.toString()); } return builder.toString(); } /** * Handles a request to start the Loader. */ @Override protected void onStartLoading() { forceLoad(); } }