Initial download manager
|
@ -46,6 +46,9 @@
|
||||||
<service android:name=".data.services.LibraryUpdateService"
|
<service android:name=".data.services.LibraryUpdateService"
|
||||||
android:exported="false"/>
|
android:exported="false"/>
|
||||||
|
|
||||||
|
<service android:name=".data.services.DownloadService"
|
||||||
|
android:exported="false"/>
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".data.services.LibraryUpdateService$SyncOnConnectionAvailable"
|
android:name=".data.services.LibraryUpdateService$SyncOnConnectionAvailable"
|
||||||
android:enabled="false">
|
android:enabled="false">
|
||||||
|
|
|
@ -0,0 +1,128 @@
|
||||||
|
package eu.kanade.mangafeed.data.helpers;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import eu.kanade.mangafeed.data.models.Chapter;
|
||||||
|
import eu.kanade.mangafeed.data.models.Manga;
|
||||||
|
import eu.kanade.mangafeed.data.models.Page;
|
||||||
|
import eu.kanade.mangafeed.events.DownloadChapterEvent;
|
||||||
|
import eu.kanade.mangafeed.sources.base.Source;
|
||||||
|
import eu.kanade.mangafeed.util.DiskUtils;
|
||||||
|
import rx.Observable;
|
||||||
|
import rx.Subscription;
|
||||||
|
import rx.schedulers.Schedulers;
|
||||||
|
import rx.subjects.PublishSubject;
|
||||||
|
|
||||||
|
public class DownloadManager {
|
||||||
|
|
||||||
|
private PublishSubject<DownloadChapterEvent> downloadsSubject;
|
||||||
|
private Subscription downloadsSubscription;
|
||||||
|
|
||||||
|
private Context context;
|
||||||
|
private SourceManager sourceManager;
|
||||||
|
|
||||||
|
public DownloadManager(Context context, SourceManager sourceManager) {
|
||||||
|
this.context = context;
|
||||||
|
this.sourceManager = sourceManager;
|
||||||
|
|
||||||
|
initializeDownloadSubscription();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeDownloadSubscription() {
|
||||||
|
if (downloadsSubscription != null && !downloadsSubscription.isUnsubscribed()) {
|
||||||
|
downloadsSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadsSubject = PublishSubject.create();
|
||||||
|
|
||||||
|
downloadsSubscription = downloadsSubject
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.concatMap(event -> downloadChapter(event.getManga(), event.getChapter()))
|
||||||
|
.onBackpressureBuffer()
|
||||||
|
.subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Observable<Page> downloadChapter(Manga manga, Chapter chapter) {
|
||||||
|
final Source source = sourceManager.get(manga.source);
|
||||||
|
final File chapterDirectory = new File(getDownloadsDirectory(), getChapterDirectory(chapter));
|
||||||
|
|
||||||
|
return source
|
||||||
|
.pullPageListFromNetwork(chapter.url)
|
||||||
|
// Ensure we don't download a chapter already downloaded
|
||||||
|
.filter(pages -> !isChapterDownloaded(chapterDirectory, pages))
|
||||||
|
// Get all the URLs to the source images, fetch pages if necessary
|
||||||
|
.flatMap(pageList -> Observable.merge(
|
||||||
|
Observable.from(pageList).filter(page -> page.getImageUrl() != null),
|
||||||
|
source.getRemainingImageUrlsFromPageList(pageList)))
|
||||||
|
// Start downloading images
|
||||||
|
.flatMap(page -> getDownloadedImage(page, source, chapterDirectory));
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getDownloadsDirectory() {
|
||||||
|
// TODO
|
||||||
|
return new File(DiskUtils.getStorageDirectories(context)[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getChapterDirectory(Chapter chapter) {
|
||||||
|
return chapter.name.replaceAll("[^a-zA-Z0-9.-]", "_");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getImageFilename(Page page) {
|
||||||
|
return page.getImageUrl().substring(
|
||||||
|
page.getImageUrl().lastIndexOf("/") + 1,
|
||||||
|
page.getImageUrl().length());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isChapterDownloaded(File chapterDir, List<Page> pages) {
|
||||||
|
return chapterDir.exists() && chapterDir.listFiles().length == pages.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isImageDownloaded(File imagePath) {
|
||||||
|
return imagePath.exists() && !imagePath.isDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Observable<Page> getDownloadedImage(final Page page, Source source, File chapterDir) {
|
||||||
|
Observable<Page> obs = Observable.just(page);
|
||||||
|
if (page.getImageUrl() == null)
|
||||||
|
return obs;
|
||||||
|
|
||||||
|
String imageFilename = getImageFilename(page);
|
||||||
|
File imagePath = new File(chapterDir, imageFilename);
|
||||||
|
|
||||||
|
if (!isImageDownloaded(imagePath)) {
|
||||||
|
page.setStatus(Page.DOWNLOAD_IMAGE);
|
||||||
|
obs = downloadImage(page, source, chapterDir, imageFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
return obs.flatMap(p -> {
|
||||||
|
page.setImagePath(imagePath.getAbsolutePath());
|
||||||
|
page.setStatus(Page.READY);
|
||||||
|
return Observable.just(page);
|
||||||
|
}).onErrorResumeNext(e -> {
|
||||||
|
page.setStatus(Page.ERROR);
|
||||||
|
return Observable.just(page);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Observable<Page> downloadImage(final Page page, Source source, File chapterDir, String imageFilename) {
|
||||||
|
return source.getImageProgressResponse(page)
|
||||||
|
.flatMap(resp -> {
|
||||||
|
try {
|
||||||
|
DiskUtils.saveBufferedSourceToDirectory(resp.body().source(), chapterDir, imageFilename);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
throw new IllegalStateException("Unable to save image");
|
||||||
|
}
|
||||||
|
return Observable.just(page);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public PublishSubject<DownloadChapterEvent> getDownloadsSubject() {
|
||||||
|
return downloadsSubject;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import android.preference.PreferenceManager;
|
||||||
|
|
||||||
import eu.kanade.mangafeed.R;
|
import eu.kanade.mangafeed.R;
|
||||||
import eu.kanade.mangafeed.sources.base.Source;
|
import eu.kanade.mangafeed.sources.base.Source;
|
||||||
|
import eu.kanade.mangafeed.util.DiskUtils;
|
||||||
|
|
||||||
public class PreferencesHelper {
|
public class PreferencesHelper {
|
||||||
|
|
||||||
|
@ -53,4 +54,9 @@ public class PreferencesHelper {
|
||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getDownloadsDirectory() {
|
||||||
|
return mPref.getString(getKey(R.string.pref_download_directory_key),
|
||||||
|
DiskUtils.getStorageDirectories(context)[0]);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
package eu.kanade.mangafeed.data.services;
|
||||||
|
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.IBinder;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import de.greenrobot.event.EventBus;
|
||||||
|
import eu.kanade.mangafeed.App;
|
||||||
|
import eu.kanade.mangafeed.data.helpers.DownloadManager;
|
||||||
|
import eu.kanade.mangafeed.events.DownloadChapterEvent;
|
||||||
|
import eu.kanade.mangafeed.util.AndroidComponentUtil;
|
||||||
|
import eu.kanade.mangafeed.util.EventBusHook;
|
||||||
|
|
||||||
|
public class DownloadService extends Service {
|
||||||
|
|
||||||
|
@Inject DownloadManager downloadManager;
|
||||||
|
|
||||||
|
public static Intent getStartIntent(Context context) {
|
||||||
|
return new Intent(context, DownloadService.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isRunning(Context context) {
|
||||||
|
return AndroidComponentUtil.isServiceRunning(context, DownloadService.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
App.get(this).getComponent().inject(this);
|
||||||
|
|
||||||
|
EventBus.getDefault().register(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
|
return START_STICKY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventBusHook
|
||||||
|
public void onEvent(DownloadChapterEvent event) {
|
||||||
|
downloadManager.getDownloadsSubject().onNext(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
EventBus.getDefault().unregister(this);
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package eu.kanade.mangafeed.events;
|
||||||
|
|
||||||
|
import eu.kanade.mangafeed.data.models.Chapter;
|
||||||
|
import eu.kanade.mangafeed.data.models.Manga;
|
||||||
|
|
||||||
|
public class DownloadChapterEvent {
|
||||||
|
private Manga manga;
|
||||||
|
private Chapter chapter;
|
||||||
|
|
||||||
|
public DownloadChapterEvent(Manga manga, Chapter chapter) {
|
||||||
|
this.manga = manga;
|
||||||
|
this.chapter = chapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Manga getManga() {
|
||||||
|
return manga;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Chapter getChapter() {
|
||||||
|
return chapter;
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ import android.app.Application;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import dagger.Component;
|
import dagger.Component;
|
||||||
|
import eu.kanade.mangafeed.data.services.DownloadService;
|
||||||
import eu.kanade.mangafeed.data.services.LibraryUpdateService;
|
import eu.kanade.mangafeed.data.services.LibraryUpdateService;
|
||||||
import eu.kanade.mangafeed.injection.module.AppModule;
|
import eu.kanade.mangafeed.injection.module.AppModule;
|
||||||
import eu.kanade.mangafeed.injection.module.DataModule;
|
import eu.kanade.mangafeed.injection.module.DataModule;
|
||||||
|
@ -42,6 +43,7 @@ public interface AppComponent {
|
||||||
void inject(Source source);
|
void inject(Source source);
|
||||||
|
|
||||||
void inject(LibraryUpdateService libraryUpdateService);
|
void inject(LibraryUpdateService libraryUpdateService);
|
||||||
|
void inject(DownloadService downloadService);
|
||||||
|
|
||||||
Application application();
|
Application application();
|
||||||
|
|
||||||
|
|
|
@ -8,11 +8,10 @@ import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
import eu.kanade.mangafeed.data.caches.CacheManager;
|
import eu.kanade.mangafeed.data.caches.CacheManager;
|
||||||
import eu.kanade.mangafeed.data.helpers.DatabaseHelper;
|
import eu.kanade.mangafeed.data.helpers.DatabaseHelper;
|
||||||
|
import eu.kanade.mangafeed.data.helpers.DownloadManager;
|
||||||
import eu.kanade.mangafeed.data.helpers.NetworkHelper;
|
import eu.kanade.mangafeed.data.helpers.NetworkHelper;
|
||||||
import eu.kanade.mangafeed.data.helpers.PreferencesHelper;
|
import eu.kanade.mangafeed.data.helpers.PreferencesHelper;
|
||||||
import eu.kanade.mangafeed.data.helpers.SourceManager;
|
import eu.kanade.mangafeed.data.helpers.SourceManager;
|
||||||
import rx.Scheduler;
|
|
||||||
import rx.schedulers.Schedulers;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provide dependencies to the DataManager, mainly Helper classes and Retrofit services.
|
* Provide dependencies to the DataManager, mainly Helper classes and Retrofit services.
|
||||||
|
@ -32,12 +31,6 @@ public class DataModule {
|
||||||
return new DatabaseHelper(app);
|
return new DatabaseHelper(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
Scheduler provideSubscribeScheduler() {
|
|
||||||
return Schedulers.io();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
CacheManager provideCacheManager(Application app) {
|
CacheManager provideCacheManager(Application app) {
|
||||||
|
@ -56,4 +49,10 @@ public class DataModule {
|
||||||
return new SourceManager(app);
|
return new SourceManager(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
DownloadManager provideDownloadManager(Application app, SourceManager sourceManager) {
|
||||||
|
return new DownloadManager(app, sourceManager);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -85,6 +85,10 @@ public class MangaChaptersPresenter extends BasePresenter<MangaChaptersFragment>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Manga getManga() {
|
||||||
|
return manga;
|
||||||
|
}
|
||||||
|
|
||||||
public void refreshChapters() {
|
public void refreshChapters() {
|
||||||
if (getView() != null)
|
if (getView() != null)
|
||||||
getView().setSwipeRefreshing();
|
getView().setSwipeRefreshing();
|
||||||
|
|
|
@ -4,6 +4,7 @@ package eu.kanade.mangafeed.sources.base;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import com.squareup.okhttp.Headers;
|
import com.squareup.okhttp.Headers;
|
||||||
|
import com.squareup.okhttp.Response;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -123,7 +124,7 @@ public abstract class Source extends BaseSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Observable<Page> cacheImage(final Page page) {
|
private Observable<Page> cacheImage(final Page page) {
|
||||||
return mNetworkService.getProgressResponse(page.getImageUrl(), mRequestHeaders, page)
|
return getImageProgressResponse(page)
|
||||||
.flatMap(resp -> {
|
.flatMap(resp -> {
|
||||||
if (!mCacheManager.putImageToDiskCache(page.getImageUrl(), resp)) {
|
if (!mCacheManager.putImageToDiskCache(page.getImageUrl(), resp)) {
|
||||||
throw new IllegalStateException("Unable to save image");
|
throw new IllegalStateException("Unable to save image");
|
||||||
|
@ -132,6 +133,10 @@ public abstract class Source extends BaseSource {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Observable<Response> getImageProgressResponse(final Page page) {
|
||||||
|
return mNetworkService.getProgressResponse(page.getImageUrl(), mRequestHeaders, page);
|
||||||
|
}
|
||||||
|
|
||||||
public void savePageList(String chapterUrl, List<Page> pages) {
|
public void savePageList(String chapterUrl, List<Page> pages) {
|
||||||
if (pages != null)
|
if (pages != null)
|
||||||
mCacheManager.putPageUrlsToDiskCache(chapterUrl, pages);
|
mCacheManager.putPageUrlsToDiskCache(chapterUrl, pages);
|
||||||
|
|
|
@ -18,8 +18,11 @@ import java.util.List;
|
||||||
|
|
||||||
import butterknife.Bind;
|
import butterknife.Bind;
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
|
import de.greenrobot.event.EventBus;
|
||||||
import eu.kanade.mangafeed.R;
|
import eu.kanade.mangafeed.R;
|
||||||
import eu.kanade.mangafeed.data.models.Chapter;
|
import eu.kanade.mangafeed.data.models.Chapter;
|
||||||
|
import eu.kanade.mangafeed.data.services.DownloadService;
|
||||||
|
import eu.kanade.mangafeed.events.DownloadChapterEvent;
|
||||||
import eu.kanade.mangafeed.presenter.MangaChaptersPresenter;
|
import eu.kanade.mangafeed.presenter.MangaChaptersPresenter;
|
||||||
import eu.kanade.mangafeed.ui.activity.MangaDetailActivity;
|
import eu.kanade.mangafeed.ui.activity.MangaDetailActivity;
|
||||||
import eu.kanade.mangafeed.ui.activity.ReaderActivity;
|
import eu.kanade.mangafeed.ui.activity.ReaderActivity;
|
||||||
|
@ -28,6 +31,8 @@ import eu.kanade.mangafeed.ui.adapter.ChaptersAdapter;
|
||||||
import eu.kanade.mangafeed.ui.fragment.base.BaseRxFragment;
|
import eu.kanade.mangafeed.ui.fragment.base.BaseRxFragment;
|
||||||
import nucleus.factory.RequiresPresenter;
|
import nucleus.factory.RequiresPresenter;
|
||||||
import rx.Observable;
|
import rx.Observable;
|
||||||
|
import rx.Subscription;
|
||||||
|
import rx.schedulers.Schedulers;
|
||||||
|
|
||||||
@RequiresPresenter(MangaChaptersPresenter.class)
|
@RequiresPresenter(MangaChaptersPresenter.class)
|
||||||
public class MangaChaptersFragment extends BaseRxFragment<MangaChaptersPresenter> implements
|
public class MangaChaptersFragment extends BaseRxFragment<MangaChaptersPresenter> implements
|
||||||
|
@ -39,6 +44,7 @@ public class MangaChaptersFragment extends BaseRxFragment<MangaChaptersPresenter
|
||||||
private ChaptersAdapter adapter;
|
private ChaptersAdapter adapter;
|
||||||
|
|
||||||
private ActionMode actionMode;
|
private ActionMode actionMode;
|
||||||
|
private Subscription downloadSubscription;
|
||||||
|
|
||||||
public static Fragment newInstance() {
|
public static Fragment newInstance() {
|
||||||
return new MangaChaptersFragment();
|
return new MangaChaptersFragment();
|
||||||
|
@ -64,6 +70,15 @@ public class MangaChaptersFragment extends BaseRxFragment<MangaChaptersPresenter
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
if (!DownloadService.isRunning(getActivity())) {
|
||||||
|
Intent intent = DownloadService.getStartIntent(getActivity());
|
||||||
|
getActivity().startService(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
inflater.inflate(R.menu.chapters, menu);
|
inflater.inflate(R.menu.chapters, menu);
|
||||||
|
@ -130,6 +145,9 @@ public class MangaChaptersFragment extends BaseRxFragment<MangaChaptersPresenter
|
||||||
case R.id.action_mark_as_unread:
|
case R.id.action_mark_as_unread:
|
||||||
getPresenter().markChaptersRead(getSelectedChapters(), false);
|
getPresenter().markChaptersRead(getSelectedChapters(), false);
|
||||||
return true;
|
return true;
|
||||||
|
case R.id.action_download:
|
||||||
|
onDownloadChapters();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -188,4 +206,20 @@ public class MangaChaptersFragment extends BaseRxFragment<MangaChaptersPresenter
|
||||||
private void setContextTitle(int count) {
|
private void setContextTitle(int count) {
|
||||||
actionMode.setTitle(getString(R.string.selected_chapters_title, count));
|
actionMode.setTitle(getString(R.string.selected_chapters_title, count));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onDownloadChapters() {
|
||||||
|
if (downloadSubscription != null && !downloadSubscription.isUnsubscribed()) {
|
||||||
|
downloadSubscription.unsubscribe();
|
||||||
|
downloadSubscription = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadSubscription = getSelectedChapters()
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.subscribe(chapter -> {
|
||||||
|
EventBus.getDefault().post(
|
||||||
|
new DownloadChapterEvent(getPresenter().getManga(), chapter));
|
||||||
|
downloadSubscription.unsubscribe();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ import okio.BufferedSource;
|
||||||
import okio.Okio;
|
import okio.Okio;
|
||||||
|
|
||||||
public final class DiskUtils {
|
public final class DiskUtils {
|
||||||
private static final Pattern DIR_SEPORATOR = Pattern.compile("/");
|
private static final Pattern DIR_SEPARATOR = Pattern.compile("/");
|
||||||
|
|
||||||
private DiskUtils() {
|
private DiskUtils() {
|
||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
|
@ -58,7 +58,7 @@ public final class DiskUtils {
|
||||||
rawUserId = "";
|
rawUserId = "";
|
||||||
} else {
|
} else {
|
||||||
final String path = Environment.getExternalStorageDirectory().getAbsolutePath();
|
final String path = Environment.getExternalStorageDirectory().getAbsolutePath();
|
||||||
final String[] folders = DIR_SEPORATOR.split(path);
|
final String[] folders = DIR_SEPARATOR.split(path);
|
||||||
final String lastFolder = folders[folders.length - 1];
|
final String lastFolder = folders[folders.length - 1];
|
||||||
boolean isDigit = false;
|
boolean isDigit = false;
|
||||||
|
|
||||||
|
@ -114,18 +114,17 @@ public final class DiskUtils {
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static File saveBufferedSourceToDirectory(BufferedSource bufferedSource, String directory, String name) throws IOException {
|
public static File saveBufferedSourceToDirectory(BufferedSource bufferedSource, File directory, String name) throws IOException {
|
||||||
File fileDirectory = new File(directory);
|
if (!directory.exists()) {
|
||||||
if (!fileDirectory.exists()) {
|
if (!directory.mkdirs()) {
|
||||||
if (!fileDirectory.mkdirs()) {
|
|
||||||
throw new IOException("Failed Creating Directory");
|
throw new IOException("Failed Creating Directory");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
File writeFile = new File(fileDirectory, name);
|
File writeFile = new File(directory, name);
|
||||||
if (writeFile.exists()) {
|
if (writeFile.exists()) {
|
||||||
if (writeFile.delete()) {
|
if (writeFile.delete()) {
|
||||||
writeFile = new File(fileDirectory, name);
|
writeFile = new File(directory, name);
|
||||||
} else {
|
} else {
|
||||||
throw new IOException("Failed Deleting Existing File for Overwrite");
|
throw new IOException("Failed Deleting Existing File for Overwrite");
|
||||||
}
|
}
|
||||||
|
|
BIN
app/src/main/res/drawable-hdpi/ic_file_download.png
Normal file
After Width: | Height: | Size: 176 B |
BIN
app/src/main/res/drawable-ldpi/ic_file_download.png
Normal file
After Width: | Height: | Size: 250 B |
BIN
app/src/main/res/drawable-mdpi/ic_file_download.png
Normal file
After Width: | Height: | Size: 131 B |
BIN
app/src/main/res/drawable-tvdpi/ic_file_download.png
Normal file
After Width: | Height: | Size: 482 B |
BIN
app/src/main/res/drawable-xhdpi/ic_file_download.png
Normal file
After Width: | Height: | Size: 178 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_file_download.png
Normal file
After Width: | Height: | Size: 232 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_file_download.png
Normal file
After Width: | Height: | Size: 307 B |
|
@ -3,6 +3,11 @@
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<item android:id="@+id/action_download"
|
||||||
|
android:title="@string/action_download"
|
||||||
|
android:icon="@drawable/ic_file_download"
|
||||||
|
app:showAsAction="ifRoom"/>
|
||||||
|
|
||||||
<item android:id="@+id/action_mark_as_read"
|
<item android:id="@+id/action_mark_as_read"
|
||||||
android:title="@string/action_mark_as_read"
|
android:title="@string/action_mark_as_read"
|
||||||
android:icon="@drawable/ic_action_done_all"
|
android:icon="@drawable/ic_action_done_all"
|
||||||
|
|
|
@ -4,4 +4,5 @@
|
||||||
<string name="pref_category_accounts_key">pref_category_accounts_key</string>
|
<string name="pref_category_accounts_key">pref_category_accounts_key</string>
|
||||||
<string name="pref_fullscreen_key">pref_fullscreen_key</string>
|
<string name="pref_fullscreen_key">pref_fullscreen_key</string>
|
||||||
<string name="pref_default_viewer_key">pref_default_viewer_key</string>
|
<string name="pref_default_viewer_key">pref_default_viewer_key</string>
|
||||||
|
<string name="pref_download_directory_key">pref_download_directory_key</string>
|
||||||
</resources>
|
</resources>
|
|
@ -86,6 +86,7 @@
|
||||||
<string name="notification_completed">Update completed</string>
|
<string name="notification_completed">Update completed</string>
|
||||||
<string name="notification_no_new_chapters">No new chapters found</string>
|
<string name="notification_no_new_chapters">No new chapters found</string>
|
||||||
<string name="notification_new_chapters">Found new chapters for:</string>
|
<string name="notification_new_chapters">Found new chapters for:</string>
|
||||||
|
<string name="action_download">Download</string>
|
||||||
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -1,85 +0,0 @@
|
||||||
package eu.kanade.mangafeed;
|
|
||||||
|
|
||||||
import android.os.Build;
|
|
||||||
|
|
||||||
import junit.framework.Assert;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.robolectric.RobolectricGradleTestRunner;
|
|
||||||
import org.robolectric.RuntimeEnvironment;
|
|
||||||
import org.robolectric.annotation.Config;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import eu.kanade.mangafeed.data.caches.CacheManager;
|
|
||||||
import eu.kanade.mangafeed.data.helpers.NetworkHelper;
|
|
||||||
import eu.kanade.mangafeed.data.models.Chapter;
|
|
||||||
import eu.kanade.mangafeed.data.models.Manga;
|
|
||||||
import eu.kanade.mangafeed.sources.Batoto;
|
|
||||||
import eu.kanade.mangafeed.sources.base.Source;
|
|
||||||
|
|
||||||
@Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.LOLLIPOP)
|
|
||||||
@RunWith(RobolectricGradleTestRunner.class)
|
|
||||||
public class BatotoTest {
|
|
||||||
|
|
||||||
NetworkHelper net;
|
|
||||||
CacheManager cache;
|
|
||||||
Source b;
|
|
||||||
final String chapterUrl ="http://bato.to/read/_/345144/minamoto-kun-monogatari_ch178_by_vortex-scans";
|
|
||||||
final String mangaUrl = "http://bato.to/comic/_/comics/natsuzora-and-run-r9597";
|
|
||||||
final String mangaUrl2 = "http://bato.to/comic/_/comics/bungaku-shoujo-to-shinitagari-no-pierrot-r534";
|
|
||||||
final String nisekoiUrl = "http://bato.to/comic/_/comics/nisekoi-r951";
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
net = new NetworkHelper();
|
|
||||||
cache = new CacheManager(RuntimeEnvironment.application.getApplicationContext());
|
|
||||||
b = new Batoto(net, cache);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testImageList() {
|
|
||||||
List<String> imageUrls = b.getRemainingImageUrlsFromPageList(chapterUrl)
|
|
||||||
.toList().toBlocking().single();
|
|
||||||
|
|
||||||
Assert.assertTrue(imageUrls.size() > 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMangaList() {
|
|
||||||
List<Manga> mangaList = b.pullPopularMangasFromNetwork(1)
|
|
||||||
.toBlocking().first();
|
|
||||||
|
|
||||||
Manga m = mangaList.get(0);
|
|
||||||
Assert.assertNotNull(m.title);
|
|
||||||
Assert.assertNotNull(m.artist);
|
|
||||||
Assert.assertNotNull(m.author);
|
|
||||||
Assert.assertNotNull(m.url);
|
|
||||||
|
|
||||||
Assert.assertTrue(mangaList.size() > 25);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testChapterList() {
|
|
||||||
List<Chapter> mangaList = b.pullChaptersFromNetwork(mangaUrl)
|
|
||||||
.toBlocking().first();
|
|
||||||
|
|
||||||
Assert.assertTrue(mangaList.size() > 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMangaDetails() {
|
|
||||||
Manga nisekoi = b.pullMangaFromNetwork(nisekoiUrl)
|
|
||||||
.toBlocking().single();
|
|
||||||
|
|
||||||
Assert.assertEquals("Nisekoi", nisekoi.title);
|
|
||||||
Assert.assertEquals("Komi Naoshi", nisekoi.author);
|
|
||||||
Assert.assertEquals("Komi Naoshi", nisekoi.artist);
|
|
||||||
Assert.assertEquals("http://bato.to/comic/_/nisekoi-r951", nisekoi.url);
|
|
||||||
Assert.assertEquals("http://img.bato.to/forums/uploads/a2a850c644a50bccc462f36922c1cbf2.jpg", nisekoi.thumbnail_url);
|
|
||||||
Assert.assertTrue(nisekoi.description.length() > 20);
|
|
||||||
Assert.assertTrue(nisekoi.genre.length() > 20);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,80 +0,0 @@
|
||||||
package eu.kanade.mangafeed;
|
|
||||||
|
|
||||||
import android.os.Build;
|
|
||||||
|
|
||||||
import junit.framework.Assert;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.robolectric.RobolectricGradleTestRunner;
|
|
||||||
import org.robolectric.RuntimeEnvironment;
|
|
||||||
import org.robolectric.annotation.Config;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import eu.kanade.mangafeed.data.caches.CacheManager;
|
|
||||||
import eu.kanade.mangafeed.data.helpers.NetworkHelper;
|
|
||||||
import eu.kanade.mangafeed.data.models.Chapter;
|
|
||||||
import eu.kanade.mangafeed.data.models.Manga;
|
|
||||||
import eu.kanade.mangafeed.sources.MangaHere;
|
|
||||||
import eu.kanade.mangafeed.sources.base.Source;
|
|
||||||
|
|
||||||
@Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.LOLLIPOP)
|
|
||||||
@RunWith(RobolectricGradleTestRunner.class)
|
|
||||||
public class MangahereTest {
|
|
||||||
|
|
||||||
NetworkHelper net;
|
|
||||||
CacheManager cache;
|
|
||||||
Source b;
|
|
||||||
final String chapterUrl ="http://www.mangahere.co/manga/kimi_ni_todoke/v15/c099/";
|
|
||||||
final String mangaUrl = "http://www.mangahere.co/manga/kimi_ni_todoke/";
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
net = new NetworkHelper();
|
|
||||||
cache = new CacheManager(RuntimeEnvironment.application.getApplicationContext());
|
|
||||||
b = new MangaHere(net, cache);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testImageList() {
|
|
||||||
List<String> imageUrls = b.getRemainingImageUrlsFromPageList(chapterUrl)
|
|
||||||
.toList().toBlocking().single();
|
|
||||||
|
|
||||||
Assert.assertTrue(imageUrls.size() > 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMangaList() {
|
|
||||||
List<Manga> mangaList = b.pullPopularMangasFromNetwork(1)
|
|
||||||
.toBlocking().first();
|
|
||||||
|
|
||||||
Manga m = mangaList.get(0);
|
|
||||||
Assert.assertNotNull(m.title);
|
|
||||||
Assert.assertNotNull(m.url);
|
|
||||||
|
|
||||||
Assert.assertTrue(mangaList.size() > 25);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testChapterList() {
|
|
||||||
List<Chapter> mangaList = b.pullChaptersFromNetwork(mangaUrl)
|
|
||||||
.toBlocking().first();
|
|
||||||
|
|
||||||
Assert.assertTrue(mangaList.size() > 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMangaDetails() {
|
|
||||||
Manga manga = b.pullMangaFromNetwork(mangaUrl)
|
|
||||||
.toBlocking().single();
|
|
||||||
|
|
||||||
Assert.assertEquals("Shiina Karuho", manga.author);
|
|
||||||
Assert.assertEquals("Shiina Karuho", manga.artist);
|
|
||||||
Assert.assertEquals("http://www.mangahere.co/manga/kimi_ni_todoke/", manga.url);
|
|
||||||
Assert.assertEquals("http://a.mhcdn.net/store/manga/4999/cover.jpg?v=1433950383", manga.thumbnail_url);
|
|
||||||
Assert.assertTrue(manga.description.length() > 20);
|
|
||||||
Assert.assertTrue(manga.genre.length() > 20);
|
|
||||||
}
|
|
||||||
}
|
|