Organize ui by feature instead of layer

This commit is contained in:
inorichi
2015-11-14 17:10:55 +01:00
parent e74f81d41a
commit 4f604e828b
61 changed files with 245 additions and 276 deletions
@@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.activity.base;
package eu.kanade.mangafeed.ui.base.activity;
import android.content.Context;
import android.support.v7.app.AppCompatActivity;
@@ -1,9 +1,10 @@
package eu.kanade.mangafeed.ui.activity.base;
package eu.kanade.mangafeed.ui.base.activity;
import android.os.Bundle;
import android.support.annotation.NonNull;
import eu.kanade.mangafeed.App;
import eu.kanade.mangafeed.ui.base.activity.BaseActivity;
import nucleus.factory.PresenterFactory;
import nucleus.factory.ReflectionPresenterFactory;
import nucleus.presenter.Presenter;
@@ -1,8 +1,8 @@
package eu.kanade.mangafeed.ui.fragment.base;
package eu.kanade.mangafeed.ui.base.fragment;
import android.support.v4.app.Fragment;
import eu.kanade.mangafeed.ui.activity.base.BaseActivity;
import eu.kanade.mangafeed.ui.base.activity.BaseActivity;
public class BaseFragment extends Fragment {
@@ -1,8 +1,9 @@
package eu.kanade.mangafeed.ui.fragment.base;
package eu.kanade.mangafeed.ui.base.fragment;
import android.os.Bundle;
import eu.kanade.mangafeed.App;
import eu.kanade.mangafeed.ui.base.fragment.BaseFragment;
import nucleus.factory.PresenterFactory;
import nucleus.factory.ReflectionPresenterFactory;
import nucleus.presenter.Presenter;
@@ -0,0 +1,36 @@
package eu.kanade.mangafeed.ui.base.presenter;
import android.os.Bundle;
import android.support.annotation.NonNull;
import de.greenrobot.event.EventBus;
import icepick.Icepick;
import nucleus.presenter.RxPresenter;
import nucleus.view.ViewWithPresenter;
public class BasePresenter<V extends ViewWithPresenter> extends RxPresenter<V> {
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
Icepick.restoreInstanceState(this, savedState);
}
@Override
protected void onSave(@NonNull Bundle state) {
super.onSave(state);
Icepick.saveInstanceState(this, state);
}
public void registerForStickyEvents() {
EventBus.getDefault().registerSticky(this);
}
public void registerForEvents() {
EventBus.getDefault().register(this);
}
public void unregisterForEvents() {
EventBus.getDefault().unregister(this);
}
}
@@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.fragment;
package eu.kanade.mangafeed.ui.catalogue;
import android.content.Intent;
import android.os.Bundle;
@@ -22,10 +22,8 @@ import butterknife.ButterKnife;
import butterknife.OnItemClick;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.models.Manga;
import eu.kanade.mangafeed.presenter.CataloguePresenter;
import eu.kanade.mangafeed.ui.activity.MangaDetailActivity;
import eu.kanade.mangafeed.ui.holder.CatalogueHolder;
import eu.kanade.mangafeed.ui.fragment.base.BaseRxFragment;
import eu.kanade.mangafeed.ui.manga.MangaActivity;
import eu.kanade.mangafeed.ui.base.fragment.BaseRxFragment;
import eu.kanade.mangafeed.util.PageBundle;
import eu.kanade.mangafeed.widget.EndlessScrollListener;
import nucleus.factory.RequiresPresenter;
@@ -125,8 +123,8 @@ public class CatalogueFragment extends BaseRxFragment<CataloguePresenter> {
public void onMangaClick(int position) {
Manga selectedManga = adapter.getItem(position);
Intent intent = MangaDetailActivity.newIntent(getActivity(), selectedManga);
intent.putExtra(MangaDetailActivity.MANGA_ONLINE, true);
Intent intent = MangaActivity.newIntent(getActivity(), selectedManga);
intent.putExtra(MangaActivity.MANGA_ONLINE, true);
startActivity(intent);
}
@@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.holder;
package eu.kanade.mangafeed.ui.catalogue;
import android.view.View;
import android.widget.ImageView;
@@ -0,0 +1,196 @@
package eu.kanade.mangafeed.ui.catalogue;
import android.os.Bundle;
import com.pushtorefresh.storio.sqlite.operations.put.PutResult;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import eu.kanade.mangafeed.data.helpers.DatabaseHelper;
import eu.kanade.mangafeed.data.helpers.SourceManager;
import eu.kanade.mangafeed.data.models.Manga;
import eu.kanade.mangafeed.sources.base.Source;
import eu.kanade.mangafeed.ui.base.presenter.BasePresenter;
import eu.kanade.mangafeed.util.PageBundle;
import eu.kanade.mangafeed.util.RxPager;
import icepick.State;
import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
import rx.subjects.PublishSubject;
public class CataloguePresenter extends BasePresenter<CatalogueFragment> {
@Inject SourceManager sourceManager;
@Inject DatabaseHelper db;
private Source selectedSource;
@State protected String mSearchName;
@State protected boolean mSearchMode;
private final int SEARCH_TIMEOUT = 1000;
private int mCurrentPage;
private RxPager pager;
private Subscription mQueryDebouncerSubscription;
private Subscription mMangaDetailFetchSubscription;
private PublishSubject<String> mQueryDebouncerSubject;
private PublishSubject<List<Manga>> mMangaDetailPublishSubject;
private static final int GET_MANGA_LIST = 1;
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
restartableReplay(GET_MANGA_LIST,
() -> pager.pages().<PageBundle<List<Manga>>>concatMap(
page -> getMangaObs(page + 1)
.map(mangas -> new PageBundle<>(page, mangas))
.observeOn(AndroidSchedulers.mainThread())
),
(view, page) -> {
view.hideProgressBar();
view.onAddPage(page);
if (mMangaDetailPublishSubject != null)
mMangaDetailPublishSubject.onNext(page.data);
});
initializeSearch();
initializeMangaDetailsLoader();
}
@Override
protected void onTakeView(CatalogueFragment view) {
super.onTakeView(view);
view.setToolbarTitle(selectedSource.getName());
if (mSearchMode)
view.restoreSearch(mSearchName);
}
public void startRequesting(int sourceId) {
selectedSource = sourceManager.get(sourceId);
restartRequest();
}
private void restartRequest() {
stop(GET_MANGA_LIST);
mCurrentPage = 1;
pager = new RxPager();
if (getView() != null)
getView().showProgressBar();
start(GET_MANGA_LIST);
}
public void requestNext() {
if (getView() != null)
getView().showGridProgressBar();
pager.requestNext(++mCurrentPage);
}
private Observable<List<Manga>> getMangaObs(int page) {
Observable<List<Manga>> obs;
if (mSearchMode)
obs = selectedSource.searchMangasFromNetwork(mSearchName, page);
else
obs = selectedSource.pullPopularMangasFromNetwork(page);
return obs.subscribeOn(Schedulers.io())
.flatMap(Observable::from)
.map(this::networkToLocalManga)
.toList();
}
private Manga networkToLocalManga(Manga networkManga) {
List<Manga> dbResult = db.getManga(networkManga.url).executeAsBlocking();
Manga localManga = !dbResult.isEmpty() ? dbResult.get(0) : null;
if (localManga == null) {
PutResult result = db.insertManga(networkManga).executeAsBlocking();
networkManga.id = result.insertedId();
localManga = networkManga;
}
return localManga;
}
private void initializeSearch() {
if (mQueryDebouncerSubscription != null)
return;
mSearchName = "";
mSearchMode = false;
mQueryDebouncerSubject = PublishSubject.create();
add(mQueryDebouncerSubscription = mQueryDebouncerSubject
.debounce(SEARCH_TIMEOUT, TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::queryFromSearch));
}
private void initializeMangaDetailsLoader() {
if (mMangaDetailFetchSubscription != null)
return;
mMangaDetailPublishSubject = PublishSubject.create();
add(mMangaDetailFetchSubscription = mMangaDetailPublishSubject
.subscribeOn(Schedulers.io())
.flatMap(Observable::from)
.filter(manga -> !manga.initialized)
.window(3)
.concatMap(pack -> pack.concatMap(this::getMangaDetails))
.filter(manga -> manga.initialized)
.onBackpressureBuffer()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(manga -> {
if (getView() != null)
getView().updateImage(manga);
}));
}
private Observable<Manga> getMangaDetails(final Manga manga) {
return selectedSource.pullMangaFromNetwork(manga.url)
.subscribeOn(Schedulers.io())
.flatMap(networkManga -> {
Manga.copyFromNetwork(manga, networkManga);
db.insertManga(manga).executeAsBlocking();
return Observable.just(manga);
});
}
public void onSearchEvent(String query, boolean now) {
// If the query is empty or not debounced, resolve it instantly
if (now || query.equals(""))
queryFromSearch(query);
else if (mQueryDebouncerSubject != null)
mQueryDebouncerSubject.onNext(query);
}
private void queryFromSearch(String query) {
// If text didn't change, do nothing
if (mSearchName.equals(query)) {
return;
}
// If going to search mode
else if (mSearchName.equals("") && !query.equals("")) {
mSearchMode = true;
}
// If going to normal mode
else if (!mSearchName.equals("") && query.equals("")) {
mSearchMode = false;
}
mSearchName = query;
restartRequest();
}
}
@@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.fragment;
package eu.kanade.mangafeed.ui.catalogue;
import android.os.Bundle;
import android.view.LayoutInflater;
@@ -13,11 +13,9 @@ import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnItemClick;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.presenter.SourcePresenter;
import eu.kanade.mangafeed.sources.base.Source;
import eu.kanade.mangafeed.ui.activity.MainActivity;
import eu.kanade.mangafeed.ui.holder.SourceHolder;
import eu.kanade.mangafeed.ui.fragment.base.BaseRxFragment;
import eu.kanade.mangafeed.ui.main.MainActivity;
import eu.kanade.mangafeed.ui.base.fragment.BaseRxFragment;
import nucleus.factory.RequiresPresenter;
import uk.co.ribot.easyadapter.EasyAdapter;
@@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.holder;
package eu.kanade.mangafeed.ui.catalogue;
import android.view.View;
import android.widget.TextView;
@@ -0,0 +1,30 @@
package eu.kanade.mangafeed.ui.catalogue;
import javax.inject.Inject;
import eu.kanade.mangafeed.data.helpers.PreferencesHelper;
import eu.kanade.mangafeed.data.helpers.SourceManager;
import eu.kanade.mangafeed.sources.base.Source;
import eu.kanade.mangafeed.ui.base.presenter.BasePresenter;
public class SourcePresenter extends BasePresenter<SourceFragment> {
@Inject SourceManager sourceManager;
@Inject PreferencesHelper prefs;
@Override
protected void onTakeView(SourceFragment view) {
super.onTakeView(view);
view.setItems(sourceManager.getSources());
}
public boolean isValidSource(Source source) {
if (!source.isLoginRequired() || source.isLogged())
return true;
return !(prefs.getSourceUsername(source).equals("")
|| prefs.getSourcePassword(source).equals(""));
}
}
@@ -1,9 +1,8 @@
package eu.kanade.mangafeed.ui.adapter;
package eu.kanade.mangafeed.ui.download;
import android.content.Context;
import eu.kanade.mangafeed.data.models.Download;
import eu.kanade.mangafeed.ui.holder.DownloadHolder;
import uk.co.ribot.easyadapter.EasyRecyclerAdapter;
public class DownloadAdapter extends EasyRecyclerAdapter<Download> {
@@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.fragment;
package eu.kanade.mangafeed.ui.download;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
@@ -15,20 +15,18 @@ import butterknife.Bind;
import butterknife.ButterKnife;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.models.Download;
import eu.kanade.mangafeed.presenter.DownloadQueuePresenter;
import eu.kanade.mangafeed.ui.adapter.DownloadAdapter;
import eu.kanade.mangafeed.ui.fragment.base.BaseRxFragment;
import eu.kanade.mangafeed.ui.base.fragment.BaseRxFragment;
import nucleus.factory.RequiresPresenter;
@RequiresPresenter(DownloadQueuePresenter.class)
public class DownloadQueueFragment extends BaseRxFragment<DownloadQueuePresenter> {
@RequiresPresenter(DownloadPresenter.class)
public class DownloadFragment extends BaseRxFragment<DownloadPresenter> {
@Bind(R.id.download_list) RecyclerView downloadList;
private LinearLayoutManager downloadListLayout;
private DownloadAdapter adapter;
public static DownloadQueueFragment newInstance() {
return new DownloadQueueFragment();
public static DownloadFragment newInstance() {
return new DownloadFragment();
}
@Override
@@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.holder;
package eu.kanade.mangafeed.ui.download;
import android.view.View;
import android.widget.ProgressBar;
@@ -0,0 +1,163 @@
package eu.kanade.mangafeed.ui.download;
import android.os.Bundle;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import eu.kanade.mangafeed.data.helpers.DownloadManager;
import eu.kanade.mangafeed.data.models.Download;
import eu.kanade.mangafeed.data.models.DownloadQueue;
import eu.kanade.mangafeed.data.models.Page;
import eu.kanade.mangafeed.ui.base.presenter.BasePresenter;
import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
import rx.subjects.PublishSubject;
import timber.log.Timber;
public class DownloadPresenter extends BasePresenter<DownloadFragment> {
@Inject DownloadManager downloadManager;
private DownloadQueue downloadQueue;
private Subscription statusSubscription;
private HashMap<Download, Subscription> progressSubscriptions;
private HashMap<Download, Subscription> pageStatusSubscriptions;
public final static int GET_DOWNLOAD_QUEUE = 1;
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
downloadQueue = downloadManager.getQueue();
progressSubscriptions = new HashMap<>();
pageStatusSubscriptions = new HashMap<>();
restartableLatestCache(GET_DOWNLOAD_QUEUE,
() -> Observable.just(downloadQueue.get()),
DownloadFragment::onNextDownloads,
(view, error) -> Timber.e(error.getMessage()));
if (savedState == null)
start(GET_DOWNLOAD_QUEUE);
}
@Override
protected void onTakeView(DownloadFragment view) {
super.onTakeView(view);
add(statusSubscription = downloadQueue.getStatusObservable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(download -> {
processStatus(download, view);
}));
}
@Override
protected void onDropView() {
destroySubscriptions();
super.onDropView();
}
private void processStatus(Download download, DownloadFragment view) {
switch (download.getStatus()) {
case Download.DOWNLOADING:
observeProgress(download, view);
observePagesStatus(download, view);
break;
case Download.DOWNLOADED:
unsubscribeProgress(download);
unsubscribePagesStatus(download);
view.updateProgress(download);
view.updateDownloadedPages(download);
break;
case Download.ERROR:
unsubscribeProgress(download);
unsubscribePagesStatus(download);
break;
}
}
private void observeProgress(Download download, DownloadFragment view) {
Subscription subscription = Observable.interval(50, TimeUnit.MILLISECONDS, Schedulers.newThread())
.flatMap(tick -> Observable.from(download.pages)
.map(Page::getProgress)
.reduce((x, y) -> x + y))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(progress -> {
if (download.totalProgress != progress) {
download.totalProgress = progress;
view.updateProgress(download);
}
});
// Avoid leaking subscriptions
Subscription oldSubscription = progressSubscriptions.remove(download);
if (oldSubscription != null) oldSubscription.unsubscribe();
progressSubscriptions.put(download, subscription);
}
private void observePagesStatus(Download download, DownloadFragment view) {
PublishSubject<Integer> pageStatusSubject = PublishSubject.create();
for (Page page : download.pages) {
if (page.getStatus() != Page.READY)
page.setStatusSubject(pageStatusSubject);
}
Subscription subscription = pageStatusSubject
.filter(status -> status == Page.READY)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(status -> {
view.updateDownloadedPages(download);
});
// Avoid leaking subscriptions
Subscription oldSubscription = pageStatusSubscriptions.remove(download);
if (oldSubscription != null) oldSubscription.unsubscribe();
pageStatusSubscriptions.put(download, subscription);
}
private void unsubscribeProgress(Download download) {
Subscription subscription = progressSubscriptions.remove(download);
if (subscription != null)
subscription.unsubscribe();
}
private void unsubscribePagesStatus(Download download) {
if (download.pages != null) {
for (Page page : download.pages)
page.setStatusSubject(null);
}
Subscription subscription = pageStatusSubscriptions.remove(download);
if (subscription != null)
subscription.unsubscribe();
}
private void destroySubscriptions() {
for (Download download : pageStatusSubscriptions.keySet()) {
for (Page page : download.pages)
page.setStatusSubject(null);
}
for (Subscription subscription : pageStatusSubscriptions.values()) {
subscription.unsubscribe();
}
pageStatusSubscriptions.clear();
for (Subscription subscription : progressSubscriptions.values()) {
subscription.unsubscribe();
}
progressSubscriptions.clear();
remove(statusSubscription);
}
}
@@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.adapter;
package eu.kanade.mangafeed.ui.library;
import android.content.Context;
import android.widget.Filter;
@@ -7,7 +7,7 @@ import android.widget.Filterable;
import java.util.List;
import eu.kanade.mangafeed.data.models.Manga;
import eu.kanade.mangafeed.ui.holder.LibraryHolder;
import eu.kanade.mangafeed.ui.library.LibraryHolder;
import rx.Observable;
import uk.co.ribot.easyadapter.EasyAdapter;
@@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.fragment;
package eu.kanade.mangafeed.ui.library;
import android.content.Intent;
import android.os.Bundle;
@@ -20,10 +20,8 @@ import butterknife.OnItemClick;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.models.Manga;
import eu.kanade.mangafeed.data.services.LibraryUpdateService;
import eu.kanade.mangafeed.presenter.LibraryPresenter;
import eu.kanade.mangafeed.ui.activity.MangaDetailActivity;
import eu.kanade.mangafeed.ui.adapter.LibraryAdapter;
import eu.kanade.mangafeed.ui.fragment.base.BaseRxFragment;
import eu.kanade.mangafeed.ui.manga.MangaActivity;
import eu.kanade.mangafeed.ui.base.fragment.BaseRxFragment;
import nucleus.factory.RequiresPresenter;
@RequiresPresenter(LibraryPresenter.class)
@@ -107,7 +105,7 @@ public class LibraryFragment extends BaseRxFragment<LibraryPresenter> {
@OnItemClick(R.id.gridView)
protected void onMangaClick(int position) {
Intent intent = MangaDetailActivity.newIntent(
Intent intent = MangaActivity.newIntent(
getActivity(),
adapter.getItem(position)
);
@@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.holder;
package eu.kanade.mangafeed.ui.library;
import android.view.View;
import android.widget.ImageView;
@@ -0,0 +1,63 @@
package eu.kanade.mangafeed.ui.library;
import android.os.Bundle;
import android.util.SparseBooleanArray;
import javax.inject.Inject;
import eu.kanade.mangafeed.data.helpers.DatabaseHelper;
import eu.kanade.mangafeed.data.helpers.PreferencesHelper;
import eu.kanade.mangafeed.ui.base.presenter.BasePresenter;
import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
public class LibraryPresenter extends BasePresenter<LibraryFragment> {
@Inject DatabaseHelper db;
@Inject PreferencesHelper prefs;
private Subscription mFavoriteMangasSubscription;
private Subscription mDeleteMangaSubscription;
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
}
@Override
protected void onTakeView(LibraryFragment view) {
super.onTakeView(view);
getFavoriteMangas();
}
public void getFavoriteMangas() {
if (mFavoriteMangasSubscription != null)
return;
add(mFavoriteMangasSubscription = db.getMangasWithUnread().createObservable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.compose(deliverLatestCache())
.subscribe(this.split(LibraryFragment::onNextMangas)));
}
public void onDelete(SparseBooleanArray checkedItems, LibraryAdapter adapter) {
if (mDeleteMangaSubscription != null)
remove(mDeleteMangaSubscription);
add(mDeleteMangaSubscription = Observable.range(0, checkedItems.size())
.observeOn(Schedulers.io())
.map(checkedItems::keyAt)
.map(adapter::getItem)
.map(manga -> {
manga.favorite = false;
return manga;
})
.toList()
.flatMap(mangas -> db.insertMangas(mangas).createObservable())
.subscribe());
}
}
@@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.activity;
package eu.kanade.mangafeed.ui.main;
import android.content.Intent;
import android.os.Bundle;
@@ -14,10 +14,11 @@ import com.mikepenz.materialdrawer.model.PrimaryDrawerItem;
import butterknife.Bind;
import butterknife.ButterKnife;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.ui.activity.base.BaseActivity;
import eu.kanade.mangafeed.ui.fragment.DownloadQueueFragment;
import eu.kanade.mangafeed.ui.fragment.LibraryFragment;
import eu.kanade.mangafeed.ui.fragment.SourceFragment;
import eu.kanade.mangafeed.ui.preference.SettingsActivity;
import eu.kanade.mangafeed.ui.base.activity.BaseActivity;
import eu.kanade.mangafeed.ui.download.DownloadFragment;
import eu.kanade.mangafeed.ui.library.LibraryFragment;
import eu.kanade.mangafeed.ui.catalogue.SourceFragment;
public class MainActivity extends BaseActivity {
@@ -75,7 +76,7 @@ public class MainActivity extends BaseActivity {
setFragment(SourceFragment.newInstance());
break;
case R.id.nav_drawer_downloads:
setFragment(DownloadQueueFragment.newInstance());
setFragment(DownloadFragment.newInstance());
break;
case R.id.nav_drawer_settings:
startActivity(new Intent(this, SettingsActivity.class));
@@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.activity;
package eu.kanade.mangafeed.ui.manga;
import android.content.Context;
import android.content.Intent;
@@ -16,14 +16,13 @@ import butterknife.Bind;
import butterknife.ButterKnife;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.models.Manga;
import eu.kanade.mangafeed.presenter.MangaDetailPresenter;
import eu.kanade.mangafeed.ui.activity.base.BaseRxActivity;
import eu.kanade.mangafeed.ui.fragment.MangaChaptersFragment;
import eu.kanade.mangafeed.ui.fragment.MangaInfoFragment;
import eu.kanade.mangafeed.ui.base.activity.BaseRxActivity;
import eu.kanade.mangafeed.ui.manga.chapter.ChaptersFragment;
import eu.kanade.mangafeed.ui.manga.info.MangaInfoFragment;
import nucleus.factory.RequiresPresenter;
@RequiresPresenter(MangaDetailPresenter.class)
public class MangaDetailActivity extends BaseRxActivity<MangaDetailPresenter> {
@RequiresPresenter(MangaPresenter.class)
public class MangaActivity extends BaseRxActivity<MangaPresenter> {
@Bind(R.id.toolbar) Toolbar toolbar;
@Bind(R.id.tabs) TabLayout tabs;
@@ -37,7 +36,7 @@ public class MangaDetailActivity extends BaseRxActivity<MangaDetailPresenter> {
public final static String MANGA_ONLINE = "manga_online";
public static Intent newIntent(Context context, Manga manga) {
Intent intent = new Intent(context, MangaDetailActivity.class);
Intent intent = new Intent(context, MangaActivity.class);
intent.putExtra(MANGA_ID, manga.id);
return intent;
}
@@ -127,7 +126,7 @@ public class MangaDetailActivity extends BaseRxActivity<MangaDetailPresenter> {
case INFO_FRAGMENT:
return MangaInfoFragment.newInstance();
case CHAPTERS_FRAGMENT:
return MangaChaptersFragment.newInstance();
return ChaptersFragment.newInstance();
default:
return null;
@@ -0,0 +1,56 @@
package eu.kanade.mangafeed.ui.manga;
import android.os.Bundle;
import javax.inject.Inject;
import de.greenrobot.event.EventBus;
import eu.kanade.mangafeed.data.helpers.DatabaseHelper;
import eu.kanade.mangafeed.data.models.Manga;
import eu.kanade.mangafeed.ui.base.presenter.BasePresenter;
import rx.Observable;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
public class MangaPresenter extends BasePresenter<MangaActivity> {
@Inject DatabaseHelper db;
private long mangaId;
private Manga manga;
private static final int DB_MANGA = 1;
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
restartableLatestCache(DB_MANGA,
() -> getDbMangaObservable()
.doOnNext(manga -> this.manga = manga),
(view, manga) -> {
view.setManga(manga);
EventBus.getDefault().postSticky(manga);
});
}
@Override
protected void onDestroy() {
super.onDestroy();
// Avoid fragments receiving wrong manga
EventBus.getDefault().removeStickyEvent(Manga.class);
}
private Observable<Manga> getDbMangaObservable() {
return db.getManga(mangaId).createObservable()
.subscribeOn(Schedulers.io())
.flatMap(Observable::from)
.observeOn(AndroidSchedulers.mainThread());
}
public void queryManga(long mangaId) {
this.mangaId = mangaId;
start(DB_MANGA);
}
}
@@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.adapter;
package eu.kanade.mangafeed.ui.manga.chapter;
import android.view.LayoutInflater;
import android.view.View;
@@ -10,9 +10,7 @@ import java.util.List;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.models.Chapter;
import eu.kanade.mangafeed.ui.fragment.MangaChaptersFragment;
import eu.kanade.mangafeed.ui.fragment.base.BaseFragment;
import eu.kanade.mangafeed.ui.holder.ChaptersHolder;
import eu.kanade.mangafeed.ui.base.fragment.BaseFragment;
public class ChaptersAdapter extends FlexibleAdapter<ChaptersHolder, Chapter> {
@@ -50,7 +48,7 @@ public class ChaptersAdapter extends FlexibleAdapter<ChaptersHolder, Chapter> {
void onListItemLongClick(int position);
}
public MangaChaptersFragment getMangaChaptersFragment() {
return (MangaChaptersFragment) fragment;
public ChaptersFragment getMangaChaptersFragment() {
return (ChaptersFragment) fragment;
}
}
@@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.fragment;
package eu.kanade.mangafeed.ui.manga.chapter;
import android.content.Intent;
import android.os.Bundle;
@@ -20,17 +20,15 @@ import butterknife.ButterKnife;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.models.Chapter;
import eu.kanade.mangafeed.data.services.DownloadService;
import eu.kanade.mangafeed.presenter.MangaChaptersPresenter;
import eu.kanade.mangafeed.ui.activity.MangaDetailActivity;
import eu.kanade.mangafeed.ui.activity.ReaderActivity;
import eu.kanade.mangafeed.ui.activity.base.BaseActivity;
import eu.kanade.mangafeed.ui.adapter.ChaptersAdapter;
import eu.kanade.mangafeed.ui.fragment.base.BaseRxFragment;
import eu.kanade.mangafeed.ui.manga.MangaActivity;
import eu.kanade.mangafeed.ui.reader.ReaderActivity;
import eu.kanade.mangafeed.ui.base.activity.BaseActivity;
import eu.kanade.mangafeed.ui.base.fragment.BaseRxFragment;
import nucleus.factory.RequiresPresenter;
import rx.Observable;
@RequiresPresenter(MangaChaptersPresenter.class)
public class MangaChaptersFragment extends BaseRxFragment<MangaChaptersPresenter> implements
@RequiresPresenter(ChaptersPresenter.class)
public class ChaptersFragment extends BaseRxFragment<ChaptersPresenter> implements
ActionMode.Callback, ChaptersAdapter.OnItemClickListener {
@Bind(R.id.chapter_list) RecyclerView chapters;
@@ -40,8 +38,8 @@ public class MangaChaptersFragment extends BaseRxFragment<MangaChaptersPresenter
private ActionMode actionMode;
public static MangaChaptersFragment newInstance() {
return new MangaChaptersFragment();
public static ChaptersFragment newInstance() {
return new ChaptersFragment();
}
@Override
@@ -103,7 +101,7 @@ public class MangaChaptersFragment extends BaseRxFragment<MangaChaptersPresenter
}
public boolean isOnlineManga() {
return ((MangaDetailActivity)getActivity()).isOnlineManga();
return ((MangaActivity)getActivity()).isOnlineManga();
}
@Override
@@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.holder;
package eu.kanade.mangafeed.ui.manga.chapter;
import android.content.Context;
import android.graphics.Color;
@@ -12,7 +12,7 @@ import butterknife.Bind;
import butterknife.ButterKnife;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.models.Chapter;
import eu.kanade.mangafeed.ui.adapter.ChaptersAdapter;
import eu.kanade.mangafeed.ui.manga.chapter.ChaptersAdapter;
public class ChaptersHolder extends RecyclerView.ViewHolder implements
View.OnClickListener, View.OnLongClickListener {
@@ -0,0 +1,166 @@
package eu.kanade.mangafeed.ui.manga.chapter;
import android.os.Bundle;
import java.io.File;
import java.util.List;
import javax.inject.Inject;
import de.greenrobot.event.EventBus;
import eu.kanade.mangafeed.data.helpers.DatabaseHelper;
import eu.kanade.mangafeed.data.helpers.DownloadManager;
import eu.kanade.mangafeed.data.helpers.PreferencesHelper;
import eu.kanade.mangafeed.data.helpers.SourceManager;
import eu.kanade.mangafeed.data.models.Chapter;
import eu.kanade.mangafeed.data.models.Manga;
import eu.kanade.mangafeed.events.ChapterCountEvent;
import eu.kanade.mangafeed.events.DownloadChaptersEvent;
import eu.kanade.mangafeed.events.SourceMangaChapterEvent;
import eu.kanade.mangafeed.sources.base.Source;
import eu.kanade.mangafeed.ui.base.presenter.BasePresenter;
import eu.kanade.mangafeed.util.EventBusHook;
import eu.kanade.mangafeed.util.PostResult;
import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
public class ChaptersPresenter extends BasePresenter<ChaptersFragment> {
@Inject DatabaseHelper db;
@Inject SourceManager sourceManager;
@Inject PreferencesHelper preferences;
@Inject DownloadManager downloadManager;
private Manga manga;
private Source source;
private static final int DB_CHAPTERS = 1;
private static final int ONLINE_CHAPTERS = 2;
private Subscription markReadSubscription;
private Subscription downloadSubscription;
private Subscription deleteSubscription;
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
restartableLatestCache(DB_CHAPTERS,
this::getDbChaptersObs,
(view, chapters) -> {
view.onNextChapters(chapters);
EventBus.getDefault().postSticky( new ChapterCountEvent(chapters.size()) );
}
);
restartableLatestCache(ONLINE_CHAPTERS,
this::getOnlineChaptersObs,
(view, result) -> view.onNextOnlineChapters()
);
}
@Override
protected void onTakeView(ChaptersFragment view) {
super.onTakeView(view);
registerForStickyEvents();
}
@Override
protected void onDropView() {
unregisterForEvents();
super.onDropView();
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().removeStickyEvent(ChapterCountEvent.class);
}
@EventBusHook
public void onEventMainThread(Manga manga) {
if (this.manga == null) {
this.manga = manga;
source = sourceManager.get(manga.source);
start(DB_CHAPTERS);
// Get chapters if it's an online source
if (getView() != null && getView().isOnlineManga()) {
refreshChapters();
}
}
}
public void refreshChapters() {
if (getView() != null)
getView().setSwipeRefreshing();
start(ONLINE_CHAPTERS);
}
private Observable<List<Chapter>> getDbChaptersObs() {
return db.getChapters(manga.id).createObservable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
private Observable<PostResult> getOnlineChaptersObs() {
return source
.pullChaptersFromNetwork(manga.url)
.subscribeOn(Schedulers.io())
.flatMap(chapters -> db.insertOrRemoveChapters(manga, chapters))
.observeOn(AndroidSchedulers.mainThread());
}
public void onChapterClicked(Chapter chapter) {
EventBus.getDefault().postSticky(new SourceMangaChapterEvent(source, manga, chapter));
}
public void markChaptersRead(Observable<Chapter> selectedChapters, boolean read) {
add(markReadSubscription = selectedChapters
.subscribeOn(Schedulers.io())
.map(chapter -> {
chapter.read = read;
return chapter;
})
.toList()
.flatMap(chapters -> db.insertChapters(chapters).createObservable())
.observeOn(AndroidSchedulers.mainThread())
.doOnCompleted(() -> remove(markReadSubscription))
.subscribe(result -> {
}));
}
public void downloadChapters(Observable<Chapter> selectedChapters) {
add(downloadSubscription = selectedChapters
.toList()
.subscribe(chapters -> {
EventBus.getDefault().postSticky(new DownloadChaptersEvent(manga, chapters));
remove(downloadSubscription);
}));
}
public void deleteChapters(Observable<Chapter> selectedChapters) {
deleteSubscription = selectedChapters
.doOnCompleted( () -> remove(deleteSubscription) )
.subscribe(chapter -> {
downloadManager.deleteChapter(source, manga, chapter);
chapter.downloaded = Chapter.NOT_DOWNLOADED;
});
}
public void checkIsChapterDownloaded(Chapter chapter) {
File dir = downloadManager.getAbsoluteChapterDirectory(source, manga, chapter);
File pageList = new File(dir, DownloadManager.PAGE_LIST_FILE);
if (dir.exists() && pageList.exists() && downloadManager
.getSavedPageList(source, manga, chapter).size() + 1 == dir.listFiles().length) {
chapter.downloaded = Chapter.DOWNLOADED;
} else {
chapter.downloaded = Chapter.NOT_DOWNLOADED;
}
}
}
@@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.fragment;
package eu.kanade.mangafeed.ui.manga.info;
import android.os.Bundle;
import android.view.LayoutInflater;
@@ -17,8 +17,7 @@ import butterknife.Bind;
import butterknife.ButterKnife;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.models.Manga;
import eu.kanade.mangafeed.presenter.MangaInfoPresenter;
import eu.kanade.mangafeed.ui.fragment.base.BaseRxFragment;
import eu.kanade.mangafeed.ui.base.fragment.BaseRxFragment;
import nucleus.factory.RequiresPresenter;
@RequiresPresenter(MangaInfoPresenter.class)
@@ -0,0 +1,73 @@
package eu.kanade.mangafeed.ui.manga.info;
import android.os.Bundle;
import javax.inject.Inject;
import eu.kanade.mangafeed.data.helpers.DatabaseHelper;
import eu.kanade.mangafeed.data.models.Manga;
import eu.kanade.mangafeed.events.ChapterCountEvent;
import eu.kanade.mangafeed.ui.base.presenter.BasePresenter;
import eu.kanade.mangafeed.util.EventBusHook;
import rx.Observable;
public class MangaInfoPresenter extends BasePresenter<MangaInfoFragment> {
@Inject DatabaseHelper db;
private Manga manga;
private int count = -1;
private static final int GET_MANGA = 1;
private static final int GET_CHAPTER_COUNT = 2;
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
restartableLatestCache(GET_MANGA,
() -> Observable.just(manga),
MangaInfoFragment::setMangaInfo);
restartableLatestCache(GET_CHAPTER_COUNT,
() -> Observable.just(count),
MangaInfoFragment::setChapterCount);
}
@Override
protected void onTakeView(MangaInfoFragment view) {
super.onTakeView(view);
registerForStickyEvents();
}
@Override
protected void onDropView() {
unregisterForEvents();
super.onDropView();
}
@EventBusHook
public void onEventMainThread(Manga manga) {
this.manga = manga;
start(GET_MANGA);
}
@EventBusHook
public void onEventMainThread(ChapterCountEvent event) {
if (count != event.getCount()) {
count = event.getCount();
start(GET_CHAPTER_COUNT);
}
}
public void initFavoriteIcon() {
if (getView() != null)
getView().setFavoriteIcon(manga.favorite);
}
public void toggleFavorite() {
manga.favorite = !manga.favorite;
db.insertManga(manga).executeAsBlocking();
}
}
@@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.dialog;
package eu.kanade.mangafeed.ui.preference;
import android.app.AlertDialog;
import android.content.Context;
@@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.fragment;
package eu.kanade.mangafeed.ui.preference;
import android.os.Bundle;
import android.preference.PreferenceFragment;
@@ -13,8 +13,7 @@ import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.helpers.PreferencesHelper;
import eu.kanade.mangafeed.data.helpers.SourceManager;
import eu.kanade.mangafeed.sources.base.Source;
import eu.kanade.mangafeed.ui.activity.base.BaseActivity;
import eu.kanade.mangafeed.ui.dialog.LoginDialogPreference;
import eu.kanade.mangafeed.ui.base.activity.BaseActivity;
import rx.Observable;
public class SettingsAccountsFragment extends PreferenceFragment {
@@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.activity;
package eu.kanade.mangafeed.ui.preference;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
@@ -7,8 +7,7 @@ import android.view.MenuItem;
import butterknife.Bind;
import butterknife.ButterKnife;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.ui.activity.base.BaseActivity;
import eu.kanade.mangafeed.ui.fragment.SettingsMainFragment;
import eu.kanade.mangafeed.ui.base.activity.BaseActivity;
public class SettingsActivity extends BaseActivity {
@@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.fragment;
package eu.kanade.mangafeed.ui.preference;
import android.os.Bundle;
import android.preference.ListPreference;
@@ -10,7 +10,7 @@ import javax.inject.Inject;
import eu.kanade.mangafeed.App;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.helpers.PreferencesHelper;
import eu.kanade.mangafeed.ui.activity.base.BaseActivity;
import eu.kanade.mangafeed.ui.base.activity.BaseActivity;
import eu.kanade.mangafeed.util.DiskUtils;
public class SettingsDownloadsFragment extends PreferenceFragment {
@@ -1,10 +1,10 @@
package eu.kanade.mangafeed.ui.fragment;
package eu.kanade.mangafeed.ui.preference;
import android.os.Bundle;
import android.preference.PreferenceFragment;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.ui.activity.base.BaseActivity;
import eu.kanade.mangafeed.ui.base.activity.BaseActivity;
public class SettingsMainFragment extends PreferenceFragment {
@@ -1,9 +1,9 @@
package eu.kanade.mangafeed.ui.fragment;
package eu.kanade.mangafeed.ui.preference;
import android.os.Bundle;
import android.preference.PreferenceFragment;
import eu.kanade.mangafeed.ui.activity.base.BaseActivity;
import eu.kanade.mangafeed.ui.base.activity.BaseActivity;
public class SettingsNestedFragment extends PreferenceFragment {
@@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.activity;
package eu.kanade.mangafeed.ui.reader;
import android.content.Context;
import android.content.Intent;
@@ -20,13 +20,12 @@ import eu.kanade.mangafeed.App;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.helpers.PreferencesHelper;
import eu.kanade.mangafeed.data.models.Page;
import eu.kanade.mangafeed.presenter.ReaderPresenter;
import eu.kanade.mangafeed.ui.activity.base.BaseRxActivity;
import eu.kanade.mangafeed.ui.viewer.LeftToRightViewer;
import eu.kanade.mangafeed.ui.viewer.RightToLeftViewer;
import eu.kanade.mangafeed.ui.viewer.VerticalViewer;
import eu.kanade.mangafeed.ui.viewer.WebtoonViewer;
import eu.kanade.mangafeed.ui.viewer.base.BaseViewer;
import eu.kanade.mangafeed.ui.base.activity.BaseRxActivity;
import eu.kanade.mangafeed.ui.reader.viewer.base.BaseReader;
import eu.kanade.mangafeed.ui.reader.viewer.horizontal.LeftToRightReader;
import eu.kanade.mangafeed.ui.reader.viewer.horizontal.RightToLeftReader;
import eu.kanade.mangafeed.ui.reader.viewer.vertical.VerticalReader;
import eu.kanade.mangafeed.ui.reader.viewer.webtoon.WebtoonReader;
import nucleus.factory.RequiresPresenter;
@RequiresPresenter(ReaderPresenter.class)
@@ -37,7 +36,7 @@ public class ReaderActivity extends BaseRxActivity<ReaderPresenter> {
@Inject PreferencesHelper prefs;
private BaseViewer viewer;
private BaseReader viewer;
private boolean isFullscreen;
private static final int LEFT_TO_RIGHT = 1;
@@ -146,16 +145,16 @@ public class ReaderActivity extends BaseRxActivity<ReaderPresenter> {
enableFullScreen();
}
private BaseViewer getViewer() {
private BaseReader getViewer() {
switch (prefs.getDefaultViewer()) {
case LEFT_TO_RIGHT: default:
return new LeftToRightViewer(this, container);
return new LeftToRightReader(this, container);
case RIGHT_TO_LEFT:
return new RightToLeftViewer(this, container);
return new RightToLeftReader(this, container);
case VERTICAL:
return new VerticalViewer(this, container);
return new VerticalReader(this, container);
case WEBTOON:
return new WebtoonViewer(this, container);
return new WebtoonReader(this, container);
}
}
@@ -0,0 +1,140 @@
package eu.kanade.mangafeed.ui.reader;
import android.os.Bundle;
import java.io.File;
import java.util.List;
import javax.inject.Inject;
import de.greenrobot.event.EventBus;
import eu.kanade.mangafeed.data.helpers.DatabaseHelper;
import eu.kanade.mangafeed.data.helpers.DownloadManager;
import eu.kanade.mangafeed.data.helpers.PreferencesHelper;
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.SourceMangaChapterEvent;
import eu.kanade.mangafeed.sources.base.Source;
import eu.kanade.mangafeed.ui.base.presenter.BasePresenter;
import eu.kanade.mangafeed.util.EventBusHook;
import icepick.State;
import rx.Observable;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
import timber.log.Timber;
public class ReaderPresenter extends BasePresenter<ReaderActivity> {
@Inject PreferencesHelper prefs;
@Inject DatabaseHelper db;
@Inject DownloadManager downloadManager;
private Source source;
private Manga manga;
private Chapter chapter;
private List<Page> pageList;
private boolean isDownloaded;
@State int currentPage;
private static final int GET_PAGE_LIST = 1;
private static final int GET_PAGE_IMAGES = 2;
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
restartableLatestCache(GET_PAGE_LIST,
() -> getPageListObservable()
.doOnNext(pages -> pageList = pages)
.doOnCompleted( () -> start(GET_PAGE_IMAGES) ),
(view, pages) -> {
view.onPageListReady(pages);
if (currentPage != 0)
view.setSelectedPage(currentPage);
},
(view, error) -> Timber.e("An error occurred while downloading page list"));
restartableReplay(GET_PAGE_IMAGES,
this::getPageImagesObservable,
(view, page) -> {},
(view, error) -> Timber.e("An error occurred while downloading an image"));
}
@Override
protected void onTakeView(ReaderActivity view) {
super.onTakeView(view);
registerForStickyEvents();
}
@Override
protected void onDropView() {
unregisterForEvents();
super.onDropView();
}
@Override
protected void onDestroy() {
if (!isDownloaded)
source.savePageList(chapter.url, pageList);
saveChapterProgress();
super.onDestroy();
}
@EventBusHook
public void onEventMainThread(SourceMangaChapterEvent event) {
source = event.getSource();
manga = event.getManga();
chapter = event.getChapter();
isDownloaded = chapter.downloaded == Chapter.DOWNLOADED;
if (chapter.last_page_read != 0 && !chapter.read)
currentPage = chapter.last_page_read;
start(GET_PAGE_LIST);
EventBus.getDefault().removeStickyEvent(SourceMangaChapterEvent.class);
}
private Observable<List<Page>> getPageListObservable() {
if (!isDownloaded)
return source.getCachedPageListOrPullFromNetwork(chapter.url)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
else
return Observable.just(downloadManager.getSavedPageList(source, manga, chapter));
}
private Observable<Page> getPageImagesObservable() {
Observable<Page> pages;
if (!isDownloaded) {
pages = Observable
.merge(Observable.from(pageList).filter(page -> page.getImageUrl() != null),
source.getRemainingImageUrlsFromPageList(pageList))
.flatMap(source::getCachedImage);
} else {
File chapterDir = downloadManager.getAbsoluteChapterDirectory(source, manga, chapter);
pages = Observable.from(pageList)
.flatMap(page -> downloadManager.getDownloadedImage(page, source, chapterDir))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
return pages
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}
private void saveChapterProgress() {
chapter.last_page_read = currentPage;
if (currentPage == pageList.size() - 1) {
chapter.read = true;
}
db.insertChapter(chapter).executeAsBlocking();
}
}
@@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.viewer.base;
package eu.kanade.mangafeed.ui.reader.viewer.base;
import android.view.MotionEvent;
import android.widget.FrameLayout;
@@ -6,15 +6,15 @@ import android.widget.FrameLayout;
import java.util.List;
import eu.kanade.mangafeed.data.models.Page;
import eu.kanade.mangafeed.ui.activity.ReaderActivity;
import eu.kanade.mangafeed.ui.reader.ReaderActivity;
public abstract class BaseViewer {
public abstract class BaseReader {
protected ReaderActivity activity;
protected FrameLayout container;
protected int currentPosition;
public BaseViewer(ReaderActivity activity, FrameLayout container) {
public BaseReader(ReaderActivity activity, FrameLayout container) {
this.activity = activity;
this.container = container;
}
@@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.adapter;
package eu.kanade.mangafeed.ui.reader.viewer.common;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
@@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.adapter;
package eu.kanade.mangafeed.ui.reader.viewer.common;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
@@ -6,13 +6,12 @@ import android.support.v4.app.FragmentManager;
import java.util.List;
import eu.kanade.mangafeed.data.models.Page;
import eu.kanade.mangafeed.ui.fragment.ReaderPageFragment;
public class ViewerPagerAdapter extends SmartFragmentStatePagerAdapter {
public class ViewPagerReaderAdapter extends SmartFragmentStatePagerAdapter {
private List<Page> pages;
public ViewerPagerAdapter(FragmentManager fragmentManager) {
public ViewPagerReaderAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
@@ -26,7 +25,7 @@ public class ViewerPagerAdapter extends SmartFragmentStatePagerAdapter {
@Override
public Fragment getItem(int position) {
return ReaderPageFragment.newInstance(pages.get(position));
return ViewPagerReaderFragment.newInstance(pages.get(position));
}
public List<Page> getPages() {
@@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.fragment;
package eu.kanade.mangafeed.ui.reader.viewer.common;
import android.os.Bundle;
import android.support.annotation.Nullable;
@@ -19,15 +19,15 @@ import butterknife.Bind;
import butterknife.ButterKnife;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.models.Page;
import eu.kanade.mangafeed.ui.activity.ReaderActivity;
import eu.kanade.mangafeed.ui.fragment.base.BaseFragment;
import eu.kanade.mangafeed.ui.base.fragment.BaseFragment;
import eu.kanade.mangafeed.ui.reader.ReaderActivity;
import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
import rx.subjects.PublishSubject;
public class ReaderPageFragment extends BaseFragment {
public class ViewPagerReaderFragment extends BaseFragment {
@Bind(R.id.page_image_view) SubsamplingScaleImageView imageView;
@Bind(R.id.progress_container) LinearLayout progressContainer;
@@ -39,8 +39,8 @@ public class ReaderPageFragment extends BaseFragment {
private Subscription progressSubscription;
private Subscription statusSubscription;
public static ReaderPageFragment newInstance(Page page) {
ReaderPageFragment fragment = new ReaderPageFragment();
public static ViewPagerReaderFragment newInstance(Page page) {
ViewPagerReaderFragment fragment = new ViewPagerReaderFragment();
fragment.setPage(page);
return fragment;
}
@@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.viewer.base;
package eu.kanade.mangafeed.ui.reader.viewer.horizontal;
import android.view.MotionEvent;
import android.widget.FrameLayout;
@@ -9,39 +9,30 @@ import butterknife.Bind;
import butterknife.ButterKnife;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.models.Page;
import eu.kanade.mangafeed.ui.activity.ReaderActivity;
import eu.kanade.mangafeed.ui.adapter.ViewerPagerAdapter;
import eu.kanade.mangafeed.widget.HorizontalViewPager;
import eu.kanade.mangafeed.ui.reader.ReaderActivity;
import eu.kanade.mangafeed.ui.reader.viewer.base.BaseReader;
import eu.kanade.mangafeed.ui.reader.viewer.common.ViewPagerReaderAdapter;
public abstract class ViewPagerViewer extends BaseViewer {
public abstract class HorizontalReader extends BaseReader {
@Bind(R.id.view_pager) HorizontalViewPager viewPager;
protected ViewerPagerAdapter adapter;
public ViewPagerViewer(ReaderActivity activity, FrameLayout container) {
protected ViewPagerReaderAdapter adapter;
public HorizontalReader(ReaderActivity activity, FrameLayout container) {
super(activity, container);
activity.getLayoutInflater().inflate(R.layout.viewer_viewpager, container);
activity.getLayoutInflater().inflate(R.layout.reader_horizontal, container);
ButterKnife.bind(this, container);
adapter = new ViewerPagerAdapter(activity.getSupportFragmentManager());
adapter = new ViewPagerReaderAdapter(activity.getSupportFragmentManager());
viewPager.setAdapter(adapter);
viewPager.setOffscreenPageLimit(3);
viewPager.addOnPageChangeListener(new HorizontalViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
viewPager.addOnPageChangeListener(new HorizontalViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
currentPosition = position;
updatePageNumber();
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
viewPager.setOnChapterBoundariesOutListener(new HorizontalViewPager.OnChapterBoundariesOutListener() {
@Override
@@ -56,11 +47,7 @@ public abstract class ViewPagerViewer extends BaseViewer {
});
viewPager.setOnChapterSingleTapListener(activity::onCenterSingleTap);
}
public HorizontalViewPager getViewPager() {
return viewPager;
}
@Override
public int getTotalPages() {
return adapter.getCount();
@@ -0,0 +1,139 @@
package eu.kanade.mangafeed.ui.reader.viewer.horizontal;
import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
public class HorizontalViewPager extends ViewPager {
private GestureDetector gestureDetector;
private OnChapterBoundariesOutListener mOnChapterBoundariesOutListener;
private OnChapterSingleTapListener mOnChapterSingleTapListener;
private static final float LEFT_REGION = 0.33f;
private static final float RIGHT_REGION = 0.66f;
private static final float SWIPE_TOLERANCE = 0.25f;
private float startDragX;
public HorizontalViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
gestureDetector = new GestureDetector(getContext(), new ReaderViewGestureListener());
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
try {
gestureDetector.onTouchEvent(ev);
if ((ev.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
if (this.getCurrentItem() == 0 || this.getCurrentItem() == this.getAdapter().getCount() - 1) {
startDragX = ev.getX();
}
}
return super.onInterceptTouchEvent(ev);
} catch (IllegalArgumentException e) {
// Do Nothing.
}
return false;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
try {
if (mOnChapterBoundariesOutListener != null) {
if (this.getCurrentItem() == 0) {
if ((ev.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {
float displacement = ev.getX() - startDragX;
if (ev.getX() > startDragX && displacement > getWidth() * SWIPE_TOLERANCE) {
mOnChapterBoundariesOutListener.onFirstPageOutEvent();
return true;
}
startDragX = 0;
}
} else if (this.getCurrentItem() == this.getAdapter().getCount() - 1) {
if ((ev.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {
float displacement = startDragX - ev.getX();
if (ev.getX() < startDragX && displacement > getWidth() * SWIPE_TOLERANCE) {
mOnChapterBoundariesOutListener.onLastPageOutEvent();
return true;
}
startDragX = 0;
}
}
}
return super.onTouchEvent(ev);
} catch (IllegalArgumentException e) {
// Do Nothing.
}
return false;
}
public boolean onImageTouch(MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
public interface OnChapterBoundariesOutListener {
void onFirstPageOutEvent();
void onLastPageOutEvent();
}
public interface OnChapterSingleTapListener {
void onSingleTap();
}
public void setOnChapterBoundariesOutListener(OnChapterBoundariesOutListener onChapterBoundariesOutListener) {
mOnChapterBoundariesOutListener = onChapterBoundariesOutListener;
}
public void setOnChapterSingleTapListener(OnChapterSingleTapListener onChapterSingleTapListener) {
mOnChapterSingleTapListener = onChapterSingleTapListener;
}
private class ReaderViewGestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
final int position = getCurrentItem();
final float positionX = e.getX();
if (positionX < getWidth() * LEFT_REGION) {
if (position != 0) {
setCurrentItem(position - 1, true);
} else {
if (mOnChapterBoundariesOutListener != null) {
mOnChapterBoundariesOutListener.onFirstPageOutEvent();
}
}
} else if (positionX > getWidth() * RIGHT_REGION) {
if (position != getAdapter().getCount() - 1) {
setCurrentItem(position + 1, true);
} else {
if (mOnChapterBoundariesOutListener != null) {
mOnChapterBoundariesOutListener.onLastPageOutEvent();
}
}
} else {
if (mOnChapterSingleTapListener != null) {
mOnChapterSingleTapListener.onSingleTap();
}
}
return true;
}
}
}
@@ -0,0 +1,23 @@
package eu.kanade.mangafeed.ui.reader.viewer.horizontal;
import android.widget.FrameLayout;
import eu.kanade.mangafeed.ui.reader.ReaderActivity;
public class LeftToRightReader extends HorizontalReader {
public LeftToRightReader(ReaderActivity activity, FrameLayout container) {
super(activity, container);
}
@Override
public void onFirstPageOut() {
// TODO
}
@Override
public void onLastPageOut() {
// TODO
}
}
@@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.viewer;
package eu.kanade.mangafeed.ui.reader.viewer.horizontal;
import android.widget.FrameLayout;
@@ -7,12 +7,11 @@ import java.util.Collections;
import java.util.List;
import eu.kanade.mangafeed.data.models.Page;
import eu.kanade.mangafeed.ui.activity.ReaderActivity;
import eu.kanade.mangafeed.ui.viewer.base.ViewPagerViewer;
import eu.kanade.mangafeed.ui.reader.ReaderActivity;
public class RightToLeftViewer extends ViewPagerViewer {
public class RightToLeftReader extends HorizontalReader {
public RightToLeftViewer(ReaderActivity activity, FrameLayout container) {
public RightToLeftReader(ReaderActivity activity, FrameLayout container) {
super(activity, container);
}
@@ -21,7 +20,7 @@ public class RightToLeftViewer extends ViewPagerViewer {
ArrayList<Page> inversedPages = new ArrayList<>(pages);
Collections.reverse(inversedPages);
adapter.setPages(inversedPages);
getViewPager().setCurrentItem(adapter.getCount()-1, false);
viewPager.setCurrentItem(adapter.getCount()-1, false);
}
@Override
@@ -31,12 +30,12 @@ public class RightToLeftViewer extends ViewPagerViewer {
@Override
public void onFirstPageOut() {
// TODO
}
@Override
public void onLastPageOut() {
// TODO
}
}
@@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.viewer;
package eu.kanade.mangafeed.ui.reader.viewer.vertical;
import android.support.v4.view.ViewPager;
import android.view.MotionEvent;
@@ -10,40 +10,30 @@ import butterknife.Bind;
import butterknife.ButterKnife;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.models.Page;
import eu.kanade.mangafeed.ui.activity.ReaderActivity;
import eu.kanade.mangafeed.ui.adapter.ViewerPagerAdapter;
import eu.kanade.mangafeed.ui.viewer.base.BaseViewer;
import fr.castorflex.android.verticalviewpager.VerticalViewPager;
import eu.kanade.mangafeed.ui.reader.ReaderActivity;
import eu.kanade.mangafeed.ui.reader.viewer.base.BaseReader;
import eu.kanade.mangafeed.ui.reader.viewer.common.ViewPagerReaderAdapter;
public class VerticalViewer extends BaseViewer {
public class VerticalReader extends BaseReader {
@Bind(R.id.view_pager) VerticalViewPager viewPager;
private ViewerPagerAdapter adapter;
public VerticalViewer(ReaderActivity activity, FrameLayout container) {
private ViewPagerReaderAdapter adapter;
public VerticalReader(ReaderActivity activity, FrameLayout container) {
super(activity, container);
activity.getLayoutInflater().inflate(R.layout.viewer_verticalviewpager, container);
activity.getLayoutInflater().inflate(R.layout.reader_vertical, container);
ButterKnife.bind(this, container);
adapter = new ViewerPagerAdapter(activity.getSupportFragmentManager());
adapter = new ViewPagerReaderAdapter(activity.getSupportFragmentManager());
viewPager.setAdapter(adapter);
viewPager.setOffscreenPageLimit(3);
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
viewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
currentPosition = position;
updatePageNumber();
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
@@ -0,0 +1,19 @@
package eu.kanade.mangafeed.ui.reader.viewer.vertical;
import android.content.Context;
import android.util.AttributeSet;
import android.view.GestureDetector;
public class VerticalViewPager extends fr.castorflex.android.verticalviewpager.VerticalViewPager {
private GestureDetector gestureDetector;
public VerticalViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
gestureDetector = new GestureDetector(getContext(), new ReaderViewGestureListener());
}
private class ReaderViewGestureListener extends GestureDetector.SimpleOnGestureListener {
// TODO
}
}
@@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.adapter;
package eu.kanade.mangafeed.ui.reader.viewer.webtoon;
import android.content.Context;
import android.view.View;
@@ -47,7 +47,7 @@ public class WebtoonAdapter extends BaseEasyRecyclerAdapter<Page> {
notifyItemChanged(page.getPageNumber());
}
@LayoutId(R.layout.item_webtoon_viewer)
@LayoutId(R.layout.item_webtoon_reader)
static class ImageViewHolder extends ItemViewHolder<Page> {
@ViewId(R.id.page_image_view) SubsamplingScaleImageView imageView;
@@ -1,4 +1,4 @@
package eu.kanade.mangafeed.ui.viewer;
package eu.kanade.mangafeed.ui.reader.viewer.webtoon;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
@@ -8,14 +8,13 @@ import android.widget.FrameLayout;
import java.util.List;
import eu.kanade.mangafeed.data.models.Page;
import eu.kanade.mangafeed.ui.activity.ReaderActivity;
import eu.kanade.mangafeed.ui.adapter.WebtoonAdapter;
import eu.kanade.mangafeed.ui.viewer.base.BaseViewer;
import eu.kanade.mangafeed.ui.reader.ReaderActivity;
import eu.kanade.mangafeed.ui.reader.viewer.base.BaseReader;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.subjects.PublishSubject;
public class WebtoonViewer extends BaseViewer {
public class WebtoonReader extends BaseReader {
private RecyclerView recycler;
private LinearLayoutManager layoutManager;
@@ -23,7 +22,7 @@ public class WebtoonViewer extends BaseViewer {
private List<Page> pages;
private Subscription subscription;
public WebtoonViewer(ReaderActivity activity, FrameLayout container) {
public WebtoonReader(ReaderActivity activity, FrameLayout container) {
super(activity, container);
recycler = new RecyclerView(activity);
@@ -1,24 +0,0 @@
package eu.kanade.mangafeed.ui.viewer;
import android.widget.FrameLayout;
import eu.kanade.mangafeed.ui.activity.ReaderActivity;
import eu.kanade.mangafeed.ui.viewer.base.ViewPagerViewer;
public class LeftToRightViewer extends ViewPagerViewer {
public LeftToRightViewer(ReaderActivity activity, FrameLayout container) {
super(activity, container);
}
@Override
public void onFirstPageOut() {
}
@Override
public void onLastPageOut() {
}
}