瀏覽代碼

Remove view logic from catalogue presenter and improve catalogue fragment

inorichi 9 年之前
父節點
當前提交
d859947c7c

+ 120 - 63
app/src/main/java/eu/kanade/mangafeed/ui/catalogue/CatalogueFragment.java

@@ -3,6 +3,7 @@ package eu.kanade.mangafeed.ui.catalogue;
 import android.content.Intent;
 import android.os.Bundle;
 import android.support.v7.widget.SearchView;
+import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
@@ -14,6 +15,7 @@ import android.widget.ImageView;
 import android.widget.ProgressBar;
 
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 import butterknife.Bind;
 import butterknife.ButterKnife;
@@ -24,7 +26,13 @@ import eu.kanade.mangafeed.ui.base.fragment.BaseRxFragment;
 import eu.kanade.mangafeed.ui.manga.MangaActivity;
 import eu.kanade.mangafeed.util.PageBundle;
 import eu.kanade.mangafeed.widget.EndlessScrollListener;
+import icepick.Icepick;
+import icepick.State;
 import nucleus.factory.RequiresPresenter;
+import rx.Subscription;
+import rx.android.schedulers.AndroidSchedulers;
+import rx.schedulers.Schedulers;
+import rx.subjects.PublishSubject;
 
 @RequiresPresenter(CataloguePresenter.class)
 public class CatalogueFragment extends BaseRxFragment<CataloguePresenter> {
@@ -35,7 +43,12 @@ public class CatalogueFragment extends BaseRxFragment<CataloguePresenter> {
 
     private CatalogueAdapter adapter;
     private EndlessScrollListener scrollListener;
-    private String search;
+
+    @State String query = "";
+    private final int SEARCH_TIMEOUT = 1000;
+
+    private PublishSubject<String> queryDebouncerSubject;
+    private Subscription queryDebouncerSubscription;
 
     public final static String SOURCE_ID = "source_id";
 
@@ -48,138 +61,182 @@ public class CatalogueFragment extends BaseRxFragment<CataloguePresenter> {
     }
 
     @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
+    public void onCreate(Bundle savedState) {
+        super.onCreate(savedState);
+        Icepick.restoreInstanceState(this, savedState);
         setHasOptionsMenu(true);
     }
 
     @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-                             Bundle savedInstanceState) {
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
         // Inflate the layout for this fragment
         View view = inflater.inflate(R.layout.fragment_catalogue, container, false);
         ButterKnife.bind(this, view);
 
-        initializeAdapter();
-        initializeScrollListener();
+        // Initialize adapter and scroll listener
+        adapter = new CatalogueAdapter(this);
+        scrollListener = new EndlessScrollListener(this::requestNextPage);
+        gridView.setAdapter(adapter);
+        gridView.setOnScrollListener(scrollListener);
 
-        int source_id = getArguments().getInt(SOURCE_ID, -1);
+        int sourceId = getArguments().getInt(SOURCE_ID, -1);
 
         showProgressBar();
+        if (savedState == null)
+            getPresenter().startRequesting(sourceId);
 
-        if (savedInstanceState == null)
-            getPresenter().startRequesting(source_id);
-
+        setToolbarTitle(getPresenter().getSource().getName());
         return view;
     }
 
     @Override
     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
         inflater.inflate(R.menu.catalogue_list, menu);
-        initializeSearch(menu);
-    }
 
-    private void initializeSearch(Menu menu) {
+        // Initialize search menu
         MenuItem searchItem = menu.findItem(R.id.action_search);
-        final SearchView sv = (SearchView) searchItem.getActionView();
-        sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+        final SearchView searchView = (SearchView) searchItem.getActionView();
+
+        if (!TextUtils.isEmpty(query)) {
+            searchItem.expandActionView();
+            searchView.setQuery(query, true);
+            searchView.clearFocus();
+        }
+        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
             @Override
             public boolean onQueryTextSubmit(String query) {
-                getPresenter().onSearchEvent(query, true);
+                onSearchEvent(query, true);
                 return true;
             }
 
             @Override
             public boolean onQueryTextChange(String newText) {
-                getPresenter().onSearchEvent(newText, false);
+                onSearchEvent(newText, false);
                 return true;
             }
         });
-        if (search != null && !search.equals("")) {
-            searchItem.expandActionView();
-            sv.setQuery(search, true);
-            sv.clearFocus();
-        }
     }
 
-    public void initializeAdapter() {
-        adapter = new CatalogueAdapter(this);
-        gridView.setAdapter(adapter);
+    @Override
+    public void onStart() {
+        super.onStart();
+        initializeSearchSubscription();
     }
 
-    @OnItemClick(R.id.gridView)
-    public void onMangaClick(int position) {
-        Manga selectedManga = adapter.getItem(position);
+    @Override
+    public void onStop() {
+        destroySearchSubscription();
+        super.onStop();
+    }
 
-        Intent intent = MangaActivity.newIntent(getActivity(), selectedManga);
-        intent.putExtra(MangaActivity.MANGA_ONLINE, true);
-        startActivity(intent);
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        Icepick.saveInstanceState(this, outState);
+        super.onSaveInstanceState(outState);
     }
 
-    public void initializeScrollListener() {
-        scrollListener = new EndlessScrollListener(this::requestNext);
-        gridView.setOnScrollListener(scrollListener);
+    private void initializeSearchSubscription() {
+        queryDebouncerSubject = PublishSubject.create();
+        queryDebouncerSubscription = queryDebouncerSubject
+                .debounce(SEARCH_TIMEOUT, TimeUnit.MILLISECONDS)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(this::restartRequest);
     }
 
-    public void requestNext() {
-        if (getPresenter().requestNext())
-            showGridProgressBar();
+    private void destroySearchSubscription() {
+        queryDebouncerSubscription.unsubscribe();
     }
 
-    public void showProgressBar() {
-        progress.setVisibility(ProgressBar.VISIBLE);
+    private void onSearchEvent(String query, boolean now) {
+        // If the query is not debounced, resolve it instantly
+        if (now)
+            restartRequest(query);
+        else if (queryDebouncerSubject != null)
+            queryDebouncerSubject.onNext(query);
     }
 
-    public void showGridProgressBar() {
-        progressGrid.setVisibility(ProgressBar.VISIBLE);
+    private void restartRequest(String newQuery) {
+        // If text didn't change, do nothing
+        if (query.equals(newQuery)) return;
+
+        query = newQuery;
+        showProgressBar();
+        // Set adapter again for scrolling to top: http://stackoverflow.com/a/17577981/3263582
+        gridView.setAdapter(adapter);
+        gridView.setSelection(0);
+
+        getPresenter().restartRequest(query);
     }
 
-    public void hideProgressBar() {
-        progress.setVisibility(ProgressBar.GONE);
-        progressGrid.setVisibility(ProgressBar.GONE);
+    private void requestNextPage() {
+        if (getPresenter().hasNextPage()) {
+            showGridProgressBar();
+            getPresenter().requestNext();
+        }
     }
 
     public void onAddPage(PageBundle<List<Manga>> page) {
         hideProgressBar();
         if (page.page == 0) {
-            gridView.setSelection(0);
             adapter.clear();
             scrollListener.resetScroll();
         }
         adapter.addAll(page.data);
     }
 
-    private int getMangaIndex(Manga manga) {
-        for (int i = adapter.getCount() - 1; i >= 0; i--) {
-            if (manga.id.equals(adapter.getItem(i).id)) {
-                return i;
-            }
+    public void onAddPageError() {
+        hideProgressBar();
+    }
+
+    @OnItemClick(R.id.gridView)
+    public void onMangaClick(int position) {
+        Manga selectedManga = adapter.getItem(position);
+
+        Intent intent = MangaActivity.newIntent(getActivity(), selectedManga);
+        intent.putExtra(MangaActivity.MANGA_ONLINE, true);
+        startActivity(intent);
+    }
+
+    public void updateImage(Manga manga) {
+        ImageView imageView = getImageView(getMangaIndex(manga));
+        if (imageView != null && manga.thumbnail_url != null) {
+            getPresenter().coverCache.loadFromNetwork(imageView, manga.thumbnail_url,
+                    getPresenter().getSource().getGlideHeaders());
         }
-        return -1;
     }
 
     private ImageView getImageView(int position) {
-        if (position == -1)
-            return null;
+        if (position == -1) return null;
 
         View v = gridView.getChildAt(position -
                 gridView.getFirstVisiblePosition());
 
-        if(v == null)
-            return null;
+        if (v == null) return null;
 
         return (ImageView) v.findViewById(R.id.thumbnail);
     }
 
-    public void updateImage(Manga manga) {
-        ImageView imageView = getImageView(getMangaIndex(manga));
-        if (imageView != null && manga.thumbnail_url != null) {
-            getPresenter().coverCache.loadFromNetwork(imageView, manga.thumbnail_url,
-                    getPresenter().getSource().getGlideHeaders());
+    private int getMangaIndex(Manga manga) {
+        for (int i = adapter.getCount() - 1; i >= 0; i--) {
+            if (manga.id.equals(adapter.getItem(i).id)) {
+                return i;
+            }
         }
+        return -1;
     }
 
-    public void restoreSearch(String mSearchName) {
-        search = mSearchName;
+    private void showProgressBar() {
+        progress.setVisibility(ProgressBar.VISIBLE);
     }
+
+    private void showGridProgressBar() {
+        progressGrid.setVisibility(ProgressBar.VISIBLE);
+    }
+
+    private void hideProgressBar() {
+        progress.setVisibility(ProgressBar.GONE);
+        progressGrid.setVisibility(ProgressBar.GONE);
+    }
+
 }

+ 26 - 88
app/src/main/java/eu/kanade/mangafeed/ui/catalogue/CataloguePresenter.java

@@ -1,11 +1,11 @@
 package eu.kanade.mangafeed.ui.catalogue;
 
 import android.os.Bundle;
+import android.text.TextUtils;
 
 import com.pushtorefresh.storio.sqlite.operations.put.PutResult;
 
 import java.util.List;
-import java.util.concurrent.TimeUnit;
 
 import javax.inject.Inject;
 
@@ -18,9 +18,7 @@ import eu.kanade.mangafeed.data.source.model.MangasPage;
 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;
@@ -32,18 +30,14 @@ public class CataloguePresenter extends BasePresenter<CatalogueFragment> {
     @Inject DatabaseHelper db;
     @Inject CoverCache coverCache;
 
-    private Source selectedSource;
+    private Source source;
 
-    @State protected String searchName;
-    @State protected boolean searchMode;
-    private final int SEARCH_TIMEOUT = 1000;
+    private String query;
 
     private int currentPage;
     private RxPager pager;
     private MangasPage lastMangasPage;
 
-    private Subscription queryDebouncerSubscription;
-    private PublishSubject<String> queryDebouncerSubject;
     private PublishSubject<List<Manga>> mangaDetailSubject;
 
     private static final int GET_MANGA_LIST = 1;
@@ -65,7 +59,10 @@ public class CataloguePresenter extends BasePresenter<CatalogueFragment> {
                     if (mangaDetailSubject != null)
                         mangaDetailSubject.onNext(page.data);
                 },
-                (view, error) -> Timber.e(error.fillInStackTrace(), error.getMessage()));
+                (view, error) -> {
+                    view.onAddPageError();
+                    Timber.e(error.getMessage());
+                });
 
         restartableLatestCache(GET_MANGA_DETAIL,
                 () -> mangaDetailSubject
@@ -77,46 +74,28 @@ public class CataloguePresenter extends BasePresenter<CatalogueFragment> {
                         .filter(manga -> manga.initialized)
                         .onBackpressureBuffer()
                         .observeOn(AndroidSchedulers.mainThread()),
-                (view, manga) -> {
-                    view.updateImage(manga);
-                },
-                (view, error) -> Timber.e(error.fillInStackTrace(), error.getMessage()));
-
-        initializeSearch();
-    }
-
-    @Override
-    protected void onTakeView(CatalogueFragment view) {
-        super.onTakeView(view);
-
-        view.setToolbarTitle(selectedSource.getName());
-
-        if (searchMode)
-            view.restoreSearch(searchName);
+                (view, manga) -> view.updateImage(manga),
+                (view, error) -> Timber.e(error.getMessage()));
     }
 
     public void startRequesting(int sourceId) {
-        selectedSource = sourceManager.get(sourceId);
-        restartRequest();
+        source = sourceManager.get(sourceId);
+        restartRequest(null);
     }
 
-    private void restartRequest() {
+    public void restartRequest(String query) {
+        this.query = query;
         stop(GET_MANGA_LIST);
         currentPage = 1;
         pager = new RxPager();
-        if (getView() != null)
-            getView().showProgressBar();
 
         start(GET_MANGA_DETAIL);
         start(GET_MANGA_LIST);
     }
 
-    public boolean requestNext() {
-        if (lastMangasPage.nextPageUrl == null)
-            return false;
-
-        pager.requestNext(++currentPage);
-        return true;
+    public void requestNext() {
+        if (hasNextPage())
+            pager.requestNext(++currentPage);
     }
 
     private Observable<List<Manga>> getMangaObs(int page) {
@@ -125,11 +104,9 @@ public class CataloguePresenter extends BasePresenter<CatalogueFragment> {
             nextMangasPage.url = lastMangasPage.nextPageUrl;
         }
 
-        Observable<MangasPage> obs;
-        if (searchMode)
-            obs = selectedSource.searchMangasFromNetwork(nextMangasPage, searchName);
-        else
-            obs = selectedSource.pullPopularMangasFromNetwork(nextMangasPage);
+        Observable<MangasPage> obs = !TextUtils.isEmpty(query) ?
+            source.searchMangasFromNetwork(nextMangasPage, query) :
+            source.pullPopularMangasFromNetwork(nextMangasPage);
 
         return obs.subscribeOn(Schedulers.io())
                 .doOnNext(mangasPage -> lastMangasPage = mangasPage)
@@ -139,7 +116,7 @@ public class CataloguePresenter extends BasePresenter<CatalogueFragment> {
     }
 
     private Manga networkToLocalManga(Manga networkManga) {
-        List<Manga> dbResult = db.getManga(networkManga.url, selectedSource.getSourceId()).executeAsBlocking();
+        List<Manga> dbResult = db.getManga(networkManga.url, source.getSourceId()).executeAsBlocking();
         Manga localManga = !dbResult.isEmpty() ? dbResult.get(0) : null;
         if (localManga == null) {
             PutResult result = db.insertManga(networkManga).executeAsBlocking();
@@ -149,62 +126,23 @@ public class CataloguePresenter extends BasePresenter<CatalogueFragment> {
         return localManga;
     }
 
-    private void initializeSearch() {
-        if (queryDebouncerSubscription != null)
-            return;
-
-        searchName = "";
-        searchMode = false;
-        queryDebouncerSubject = PublishSubject.create();
-
-        add(queryDebouncerSubscription = queryDebouncerSubject
-                .debounce(SEARCH_TIMEOUT, TimeUnit.MILLISECONDS)
-                .subscribeOn(Schedulers.io())
-                .observeOn(AndroidSchedulers.mainThread())
-                .subscribe(this::queryFromSearch));
-    }
-
     private Observable<Manga> getMangaDetails(final Manga manga) {
-        return selectedSource.pullMangaFromNetwork(manga.url)
+        return source.pullMangaFromNetwork(manga.url)
                 .subscribeOn(Schedulers.io())
                 .flatMap(networkManga -> {
                     Manga.copyFromNetwork(manga, networkManga);
                     db.insertManga(manga).executeAsBlocking();
                     return Observable.just(manga);
                 })
-                .onErrorResumeNext(error -> {
-                    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 (queryDebouncerSubject != null)
-            queryDebouncerSubject.onNext(query);
+                .onErrorResumeNext(error -> Observable.just(manga));
     }
 
-    private void queryFromSearch(String query) {
-        // If text didn't change, do nothing
-        if (searchName.equals(query)) {
-            return;
-        }
-        // If going to search mode
-        else if (searchName.equals("") && !query.equals("")) {
-            searchMode = true;
-        }
-        // If going to normal mode
-        else if (!searchName.equals("") && query.equals("")) {
-            searchMode = false;
-        }
-
-        searchName = query;
-        restartRequest();
+    public Source getSource() {
+        return source;
     }
 
-    public Source getSource() {
-        return selectedSource;
+    public boolean hasNextPage() {
+        return lastMangasPage != null && lastMangasPage.nextPageUrl != null;
     }
 
 }

+ 2 - 2
app/src/main/java/eu/kanade/mangafeed/ui/manga/chapter/ChaptersPresenter.java

@@ -133,8 +133,8 @@ public class ChaptersPresenter extends BasePresenter<ChaptersFragment> {
             observable = observable.filter(chapter -> chapter.status == Download.DOWNLOADED);
         }
         return observable.toSortedList((chapter, chapter2) -> sortOrderAToZ ?
-                Float.compare(chapter.chapter_number, chapter2.chapter_number) :
-                Float.compare(chapter2.chapter_number, chapter.chapter_number));
+                Float.compare(chapter2.chapter_number, chapter.chapter_number) :
+                Float.compare(chapter.chapter_number, chapter2.chapter_number));
     }
 
     private void setChapterStatus(Chapter chapter) {