/* * Copyright (c) 2015 Ha Duy Trung * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.github.hidroh.materialistic.data; import android.content.Context; import android.support.annotation.Keep; import android.support.annotation.NonNull; import android.text.format.DateUtils; import java.io.IOException; import javax.inject.Inject; import javax.inject.Named; import io.github.hidroh.materialistic.ActivityModule; import io.github.hidroh.materialistic.Preferences; import io.github.hidroh.materialistic.annotation.Synthetic; import retrofit2.Call; import retrofit2.http.GET; import retrofit2.http.Query; import rx.Observable; import rx.android.schedulers.AndroidSchedulers; public class AlgoliaClient implements ItemManager { public static boolean sSortByTime = true; public static final String HOST = "hn.algolia.com"; private static final String BASE_API_URL = "https://" + HOST + "/api/v1/"; static final String MIN_CREATED_AT = "created_at_i>"; RestService mRestService; @Inject @Named(ActivityModule.HN) ItemManager mHackerNewsClient; @Inject public AlgoliaClient(Context context, RestServiceFactory factory) { mRestService = factory.rxEnabled(true).create(BASE_API_URL, RestService.class); sSortByTime = Preferences.isSortByRecent(context); } @Override public void getStories(String filter, @CacheMode int cacheMode, final ResponseListener<Item[]> listener) { if (listener == null) { return; } search(filter) .map(this::toItems) .observeOn(AndroidSchedulers.mainThread()) .subscribe(listener::onResponse, t -> listener.onError(t != null ? t.getMessage() : "")); } @Override public void getItem(String itemId, @CacheMode int cacheMode, ResponseListener<Item> listener) { mHackerNewsClient.getItem(itemId, cacheMode, listener); } @Override public Item[] getStories(String filter, @CacheMode int cacheMode) { long timestamp = System.currentTimeMillis() - DateUtils.DAY_IN_MILLIS; // since yesterday try { return toItems(mRestService.search(filter, MIN_CREATED_AT + timestamp / 1000) .execute() .body()); } catch (IOException e) { return new Item[0]; } } @Override public Item getItem(String itemId, @CacheMode int cacheMode) { return mHackerNewsClient.getItem(itemId, cacheMode); } protected Observable<AlgoliaHits> search(String filter) { // TODO add ETag header return sSortByTime ? mRestService.searchByDate(filter) : mRestService.search(filter); } @NonNull private Item[] toItems(AlgoliaHits algoliaHits) { if (algoliaHits == null) { return new Item[0]; } Hit[] hits = algoliaHits.hits; Item[] stories = new Item[hits == null ? 0 : hits.length]; for (int i = 0; i < stories.length; i++) { //noinspection ConstantConditions HackerNewsItem item = new HackerNewsItem( Long.parseLong(hits[i].objectID)); item.rank = i + 1; stories[i] = item; } return stories; } interface RestService { @GET("search_by_date?hitsPerPage=100&tags=story&attributesToRetrieve=objectID&attributesToHighlight=none") Observable<AlgoliaHits> searchByDate(@Query("query") String query); @GET("search?hitsPerPage=100&tags=story&attributesToRetrieve=objectID&attributesToHighlight=none") Observable<AlgoliaHits> search(@Query("query") String query); @GET("search?hitsPerPage=100&tags=story&attributesToRetrieve=objectID&attributesToHighlight=none") Observable<AlgoliaHits> searchByMinTimestamp(@Query("numericFilters") String timestampSeconds); @GET("search?hitsPerPage=10&tags=story&attributesToRetrieve=objectID&attributesToHighlight=none") Call<AlgoliaHits> search(@Query("query") String query, @Query("numericFilters") String timestampSeconds); } static class AlgoliaHits { @Keep @Synthetic Hit[] hits; } static class Hit { @Keep @Synthetic String objectID; } }