/*
* 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.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.support.annotation.Keep;
import android.support.annotation.NonNull;
import android.support.annotation.WorkerThread;
import android.text.TextUtils;
import javax.inject.Inject;
import io.github.hidroh.materialistic.BuildConfig;
import io.github.hidroh.materialistic.annotation.Synthetic;
import retrofit2.http.GET;
import retrofit2.http.Headers;
import retrofit2.http.Query;
import rx.Observable;
import rx.Scheduler;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
public interface ReadabilityClient {
String HOST = "mercury.postlight.com";
interface Callback {
void onResponse(String content);
}
void parse(String itemId, String url, Callback callback);
@WorkerThread
void parse(String itemId, String url);
class Impl implements ReadabilityClient {
private static final CharSequence EMPTY_CONTENT = "<div></div>";
private final MercuryService mMercuryService;
private final ContentResolver mContentResolver;
private final Scheduler mIoScheduler;
interface MercuryService {
String MERCURY_API_URL = "https://" + HOST + "/";
String X_API_KEY = "x-api-key: ";
@Headers({RestServiceFactory.CACHE_CONTROL_MAX_AGE_24H,
X_API_KEY + BuildConfig.MERCURY_TOKEN})
@GET("parser")
Observable<Readable> parse(@Query("url") String url);
}
class Readable {
@Keep @Synthetic
String content;
}
@Inject
public Impl(Context context, RestServiceFactory factory, Scheduler ioScheduler) {
mMercuryService = factory.rxEnabled(true)
.create(MercuryService.MERCURY_API_URL,
MercuryService.class);
mContentResolver = context.getContentResolver();
mIoScheduler = ioScheduler;
}
@Override
public void parse(String itemId, String url, Callback callback) {
Observable.defer(() -> fromCache(itemId))
.subscribeOn(mIoScheduler)
.flatMap(content -> content != null ?
Observable.just(content) : fromNetwork(itemId, url))
.map(content -> TextUtils.equals(EMPTY_CONTENT, content) ? null : content)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(callback::onResponse);
}
@WorkerThread
@Override
public void parse(String itemId, String url) {
Observable.defer(() -> fromCache(itemId))
.subscribeOn(Schedulers.immediate())
.switchIfEmpty(fromNetwork(itemId, url))
.map(content -> TextUtils.equals(EMPTY_CONTENT, content) ? null : content)
.observeOn(Schedulers.immediate())
.subscribe();
}
@NonNull
private Observable<String> fromNetwork(String itemId, String url) {
return mMercuryService.parse(url)
.onErrorReturn(throwable -> null)
.map(readable -> readable == null ? null : readable.content)
.doOnNext(content -> cache(itemId, content));
}
private Observable<String> fromCache(String itemId) {
Cursor cursor = mContentResolver.query(MaterialisticProvider.URI_READABILITY,
new String[]{MaterialisticProvider.ReadabilityEntry.COLUMN_NAME_CONTENT},
MaterialisticProvider.ReadabilityEntry.COLUMN_NAME_ITEM_ID + " = ?",
new String[]{itemId}, null);
String content = null;
if (cursor != null) {
if (cursor.moveToFirst()) {
content = cursor.getString(cursor.getColumnIndexOrThrow(
MaterialisticProvider.ReadabilityEntry.COLUMN_NAME_CONTENT));
}
cursor.close();
}
return Observable.just(content);
}
@WorkerThread
private void cache(String itemId, String content) {
final ContentValues contentValues = new ContentValues();
contentValues.put(MaterialisticProvider.ReadabilityEntry.COLUMN_NAME_ITEM_ID, itemId);
contentValues.put(MaterialisticProvider.ReadabilityEntry.COLUMN_NAME_CONTENT, content);
mContentResolver.insert(MaterialisticProvider.URI_READABILITY, contentValues);
}
}
}