package moe.kurumi.moegallery.data; import android.content.Context; import android.net.Uri; import android.support.annotation.UiThread; import com.raizlabs.android.dbflow.sql.builder.Condition; import com.raizlabs.android.dbflow.sql.language.Select; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.inject.Inject; import javax.inject.Named; import moe.kurumi.moegallery.application.Application; import moe.kurumi.moegallery.model.AnimePictures; import moe.kurumi.moegallery.model.AnimePicturesList; import moe.kurumi.moegallery.model.Behoimi; import moe.kurumi.moegallery.model.Config; import moe.kurumi.moegallery.model.Danbooru; import moe.kurumi.moegallery.model.DanbooruTag; import moe.kurumi.moegallery.model.Gelbooru; import moe.kurumi.moegallery.model.GelbooruList; import moe.kurumi.moegallery.model.Github; import moe.kurumi.moegallery.model.GithubRelease; import moe.kurumi.moegallery.model.Image; import moe.kurumi.moegallery.model.Moebooru; import moe.kurumi.moegallery.model.Tag; import moe.kurumi.moegallery.model.Version; import moe.kurumi.moegallery.model.database.FavoriteImage; import moe.kurumi.moegallery.model.database.FavoriteImage$Table; import moe.kurumi.moegallery.model.database.HistoryImage; import moe.kurumi.moegallery.model.database.HistoryImage$Table; import moe.kurumi.moegallery.model.setting.Setting; import moe.kurumi.moegallery.utils.Utils; import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.RequestBody; import okio.BufferedSink; import okio.Okio; import retrofit2.Retrofit; import rx.Observable; import rx.Subscriber; import rx.android.schedulers.AndroidSchedulers; import rx.schedulers.Schedulers; public class ImageRepository implements ImageDataSource { public static final int LIMIT = 20; @Inject Setting mSetting; @Inject Context context; @Inject Retrofit.Builder mBuilder; @Inject @Named("xml") Retrofit.Builder mXmlBuilder; private List<Image> mImageList = new ArrayList<>(); private Map<String, Image> mDetailImageMap = new HashMap<>(); private Map<String, Uri> mImageUri = new HashMap<>(); private OnListUpdateListener listUpdateListener; private int page = 0; private ImageRepository() { Application.getAppComponent().inject(this); } public static ImageRepository getInstance() { return SingletonHelper.INSTANCE; } @Override public Observable<List<? extends Image>> loadList(final String tags) { return Observable.create(new Observable.OnSubscribe<List<? extends Image>>() { @Override public void call(Subscriber<? super List<? extends Image>> subscriber) { try { page++; String apiUri = mSetting.provider(); Retrofit restAdapter = mBuilder .baseUrl(apiUri) .build(); List<? extends Image> images; switch (apiUri) { case Providers.DANBOORU_URI: Danbooru danbooru = restAdapter.create(Danbooru.class); images = danbooru.list(LIMIT, page, tags).execute().body(); break; case Providers.KONACHAN_URI: case Providers.YANDERE_URI: Moebooru moebooru = restAdapter.create(Moebooru.class); images = moebooru.list(LIMIT, page, tags.isEmpty() ? "*" : tags) .execute() .body(); break; case Providers.BEHOIMI_URI: Behoimi behoimi = restAdapter.create(Behoimi.class); images = behoimi.list(LIMIT, page, tags).execute().body(); break; case Providers.ANIME_PICTURES_URI: AnimePictures animePictures = restAdapter.create(AnimePictures.class); AnimePicturesList animePicturesList; if (tags.isEmpty()) { animePicturesList = animePictures.list(page - 1, "json", "en") .execute() .body(); } else { animePicturesList = animePictures.search(page - 1, tags, "date", 0, "json", "en").execute().body(); } images = animePicturesList.getPreviews(); break; case Providers.GELBOORU_URI: restAdapter = mXmlBuilder .baseUrl(apiUri) .build(); Gelbooru gelbooru = restAdapter.create(Gelbooru.class); GelbooruList gelbooruList = gelbooru.list(LIMIT, page - 1, tags) .execute() .body(); images = gelbooruList.getPost(); break; default: images = new ArrayList<>(); } for (Image image : images) { mImageList.add(image); } subscriber.onNext(images); notifyDataSetChanged(); } catch (Exception e) { e.printStackTrace(); page--; subscriber.onError(e); } } }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()); } @Override public Observable<List<? extends Tag>> listTag(final String tag) { return Observable.create(new Observable.OnSubscribe<List<? extends Tag>>() { @Override public void call(Subscriber<? super List<? extends Tag>> subscriber) { try { String apiUri = mSetting.provider(); List<? extends Tag> tags; Retrofit restAdapter = mBuilder .baseUrl(apiUri) //.setLogLevel(RestAdapter.LogLevel.FULL) .build(); switch (apiUri) { case Providers.KONACHAN_URI: case Providers.YANDERE_URI: Moebooru moebooru = restAdapter.create(Moebooru.class); tags = moebooru.tag(tag.trim().replace(' ', '_')) .execute() .body(); break; case Providers.DANBOORU_URI: Danbooru danbooru = restAdapter.create(Danbooru.class); List<DanbooruTag> tagsStart = danbooru.tag("*" + tag.trim()) .execute() .body(); List<DanbooruTag> tagsEnd = danbooru.tag(tag.trim() + "*") .execute() .body(); tagsStart.addAll(tagsEnd); tags = tagsStart; break; case Providers.BEHOIMI_URI: Behoimi behoimi = restAdapter.create(Behoimi.class); tags = behoimi.tag("*" + tag.trim().replace(' ', '_') + "*") .execute() .body(); break; case Providers.ANIME_PICTURES_URI: AnimePictures animePictures = restAdapter.create( AnimePictures.class); tags = animePictures.tag( RequestBody.create(MediaType.parse("text/plain"), tag)) .execute() .body() .getTagsList(); break; case Providers.GELBOORU_URI: restAdapter = mBuilder .baseUrl(apiUri) //.setLogLevel(RestAdapter.LogLevel.FULL) .build(); Gelbooru gelbooru = restAdapter.create(Gelbooru.class); tags = gelbooru.tag(200, 0, tag.trim().replace(' ', '_')) .execute() .body() .getTag(); break; default: tags = new ArrayList<>(); } subscriber.onNext(tags); } catch (IOException e) { e.printStackTrace(); subscriber.onError(e); } } }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()); } @Override public Image get(int position) { return mImageList.get(position); } @Override public int size() { return mImageList.size(); } @Override public int getCount() { int count; String apiUri = mSetting.provider(); switch (apiUri) { case Providers.ANIME_PICTURES_URI: count = 80 * page; break; default: count = LIMIT * page; } return count; } @Override public void clear() { mImageList.clear(); page = 0; } @Override public void cacheDetail(String key, Image image) { mDetailImageMap.put(key, image); } @Override public Image getCachedDetail(String key) { return mDetailImageMap.get(key); } @Override public void cacheImageUri(String key, Uri uri) { mImageUri.put(key, uri); } @Override public Uri getImageUri(String key) { return mImageUri.get(key); } @Override public Observable<List<? extends Image>> loadListFromHistory() { return Observable.create(new Observable.OnSubscribe<List<? extends Image>>() { @Override public void call(Subscriber<? super List<? extends Image>> subscriber) { try { String providerUri = mSetting.provider(). replace(Providers.SCHEME_HTTPS, ""). replace(Providers.SCHEME_HTTP, ""); List<HistoryImage> historyImages = new Select().from(HistoryImage.class).where( Condition.column(HistoryImage$Table.PREVIEWURL) .like("%" + providerUri + "%")). orderBy(false, HistoryImage$Table.LAST).queryList(); for (Image image : historyImages) { mImageList.add(image); } subscriber.onNext(mImageList); notifyDataSetChanged(); } catch (Exception e) { e.printStackTrace(); subscriber.onError(e); } } }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()); } @Override public Observable<List<? extends Image>> loadListFromFavorite() { return Observable.create(new Observable.OnSubscribe<List<? extends Image>>() { @Override public void call(Subscriber<? super List<? extends Image>> subscriber) { try { String providerUri = mSetting.provider(). replace(Providers.SCHEME_HTTPS, ""). replace(Providers.SCHEME_HTTP, ""); List<FavoriteImage> favoriteImages = new Select().from(FavoriteImage.class) .where( Condition.column(FavoriteImage$Table.PREVIEWURL) .like("%" + providerUri + "%")) . orderBy(false, FavoriteImage$Table.LAST) .queryList(); for (Image image : favoriteImages) { mImageList.add(image); } subscriber.onNext(mImageList); notifyDataSetChanged(); } catch (Exception e) { e.printStackTrace(); subscriber.onError(e); } } }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()); } @Override public Observable<GithubRelease.Asset> checkUpdate(final String versionString) { return Observable.create(new Observable.OnSubscribe<GithubRelease.Asset>() { @Override public void call(Subscriber<? super GithubRelease.Asset> subscriber) { long lastUpdate = mSetting.lastUpdate(); if (System.currentTimeMillis() - lastUpdate > Config.UPDATE_DURATION) { try { Retrofit restAdapter = mBuilder.baseUrl(Providers.GITHUB_API_URI).build(); Github github = restAdapter.create(Github.class); GithubRelease latest = github.latest().execute().body(); Version currentVersion = new Version(versionString); Version latestVersion = new Version(latest.getTagName().substring(1)); if (latestVersion.compareTo(currentVersion) > 0 && !latest.getPrerelease() && latest.getAuthor().getLogin().equals(Config.GITHUB_UPDATE_AUTHOR)) { for (GithubRelease.Asset asset : latest.getAssets()) { if (asset.getContentType().equals(Config.GITHUB_UPDATE_CONTENT_TYPE) && asset.getState().equals(Config.GITHUB_UPDATE_STATUS)) { subscriber.onNext(asset); } } } mSetting.setLastUpdate(System.currentTimeMillis()); } catch (Exception e) { e.printStackTrace(); subscriber.onError(e); } } } }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()); } @Override public Observable<Uri> downloadUpdate(final File dir, final String name, final String url) { return Observable.create(new Observable.OnSubscribe<Uri>() { @Override public void call(Subscriber<? super Uri> subscriber) { try { File downloadedFile = new File(dir, name); OkHttpClient client = new OkHttpClient(); okhttp3.Request request = new okhttp3.Request.Builder().url( Utils.fixURL(url)).build(); okhttp3.Response response = client.newCall(request).execute(); BufferedSink sink = Okio.buffer(Okio.sink(downloadedFile)); sink.writeAll(response.body().source()); sink.close(); subscriber.onNext(Uri.fromFile(downloadedFile)); } catch (Exception e) { e.printStackTrace(); subscriber.onError(e); } } }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()); } @UiThread void notifyDataSetChanged() { if (listUpdateListener != null) { listUpdateListener.OnListUpdate(); } } @UiThread void onError(String message) { if (listUpdateListener != null) { listUpdateListener.OnError(context, message); } } private final static class SingletonHelper { private final static ImageRepository INSTANCE = new ImageRepository(); } }