Catalogue now has a dropdown menu to select or change the source

This commit is contained in:
inorichi 2015-12-14 16:51:12 +01:00
parent 8dca7fe79a
commit 4630a5ed1a
20 changed files with 233 additions and 205 deletions

View File

@ -88,17 +88,17 @@ public class PreferencesHelper {
}
public String getSourceUsername(Source source) {
return prefs.getString(SOURCE_ACCOUNT_USERNAME + source.getSourceId(), "");
return prefs.getString(SOURCE_ACCOUNT_USERNAME + source.getId(), "");
}
public String getSourcePassword(Source source) {
return prefs.getString(SOURCE_ACCOUNT_PASSWORD + source.getSourceId(), "");
return prefs.getString(SOURCE_ACCOUNT_PASSWORD + source.getId(), "");
}
public void setSourceCredentials(Source source, String username, String password) {
prefs.edit()
.putString(SOURCE_ACCOUNT_USERNAME + source.getSourceId(), username)
.putString(SOURCE_ACCOUNT_PASSWORD + source.getSourceId(), password)
.putString(SOURCE_ACCOUNT_USERNAME + source.getId(), username)
.putString(SOURCE_ACCOUNT_PASSWORD + source.getId(), password)
.apply();
}

View File

@ -19,21 +19,21 @@ public class SourceManager {
public static final int MANGAFOX = 3;
public static final int KISSMANGA = 4;
private HashMap<Integer, Source> mSourcesMap;
private HashMap<Integer, Source> sourcesMap;
private Context context;
public SourceManager(Context context) {
mSourcesMap = new HashMap<>();
sourcesMap = new HashMap<>();
this.context = context;
initializeSources();
}
public Source get(int sourceKey) {
if (!mSourcesMap.containsKey(sourceKey)) {
mSourcesMap.put(sourceKey, createSource(sourceKey));
if (!sourcesMap.containsKey(sourceKey)) {
sourcesMap.put(sourceKey, createSource(sourceKey));
}
return mSourcesMap.get(sourceKey);
return sourcesMap.get(sourceKey);
}
private Source createSource(int sourceKey) {
@ -52,14 +52,14 @@ public class SourceManager {
}
private void initializeSources() {
mSourcesMap.put(BATOTO, createSource(BATOTO));
mSourcesMap.put(MANGAHERE, createSource(MANGAHERE));
mSourcesMap.put(MANGAFOX, createSource(MANGAFOX));
mSourcesMap.put(KISSMANGA, createSource(KISSMANGA));
sourcesMap.put(BATOTO, createSource(BATOTO));
sourcesMap.put(MANGAHERE, createSource(MANGAHERE));
sourcesMap.put(MANGAFOX, createSource(MANGAFOX));
sourcesMap.put(KISSMANGA, createSource(KISSMANGA));
}
public List<Source> getSources() {
return new ArrayList<>(mSourcesMap.values());
return new ArrayList<>(sourcesMap.values());
}
}

View File

@ -18,7 +18,7 @@ public abstract class BaseSource {
public abstract String getName();
// Id of the source (must be declared and obtained from SourceManager to avoid conflicts)
public abstract int getSourceId();
public abstract int getId();
// Base url of the source, like: http://example.com
public abstract String getBaseUrl();
@ -95,4 +95,8 @@ public abstract class BaseSource {
return builder;
}
@Override
public String toString() {
return getName();
}
}

View File

@ -34,6 +34,8 @@ public abstract class Source extends BaseSource {
protected Headers requestHeaders;
protected LazyHeaders glideHeaders;
public Source() {}
public Source(Context context) {
App.get(context).getComponent().inject(this);
requestHeaders = headersBuilder().build();
@ -188,7 +190,7 @@ public abstract class Source extends BaseSource {
}
protected String getChapterCacheKey(String chapterUrl) {
return getSourceId() + chapterUrl;
return getId() + chapterUrl;
}
protected LazyHeaders.Builder glideHeadersBuilder() {

View File

@ -70,7 +70,7 @@ public class Batoto extends Source {
}
@Override
public int getSourceId() {
public int getId() {
return SourceManager.BATOTO;
}
@ -218,7 +218,7 @@ public class Batoto extends Source {
Element urlElement = htmlBlock.select("a[href^=http://bato.to]").first();
Element updateElement = htmlBlock.select("td").get(5);
mangaFromHtmlBlock.source = getSourceId();
mangaFromHtmlBlock.source = getId();
if (urlElement != null) {
mangaFromHtmlBlock.setUrl(urlElement.attr("href"));

View File

@ -55,7 +55,7 @@ public class Kissmanga extends Source {
}
@Override
public int getSourceId() {
public int getId() {
return SourceManager.KISSMANGA;
}
@ -94,7 +94,7 @@ public class Kissmanga extends Source {
private Manga constructPopularMangaFromHtmlBlock(Element htmlBlock) {
Manga mangaFromHtmlBlock = new Manga();
mangaFromHtmlBlock.source = getSourceId();
mangaFromHtmlBlock.source = getId();
Element urlElement = htmlBlock.select("td a:eq(0)").first();

View File

@ -40,7 +40,7 @@ public class Mangafox extends Source {
}
@Override
public int getSourceId() {
public int getId() {
return SourceManager.MANGAFOX;
}
@ -79,7 +79,7 @@ public class Mangafox extends Source {
private Manga constructPopularMangaFromHtmlBlock(Element htmlBlock) {
Manga mangaFromHtmlBlock = new Manga();
mangaFromHtmlBlock.source = getSourceId();
mangaFromHtmlBlock.source = getId();
Element urlElement = htmlBlock.select("a.title").first();
@ -115,7 +115,7 @@ public class Mangafox extends Source {
private Manga constructSearchMangaFromHtmlBlock(Element htmlBlock) {
Manga mangaFromHtmlBlock = new Manga();
mangaFromHtmlBlock.source = getSourceId();
mangaFromHtmlBlock.source = getId();
Element urlElement = htmlBlock.select("a.series_preview").first();

View File

@ -40,7 +40,7 @@ public class Mangahere extends Source {
}
@Override
public int getSourceId() {
public int getId() {
return SourceManager.MANGAHERE;
}
@ -117,7 +117,7 @@ public class Mangahere extends Source {
private Manga constructPopularMangaFromHtmlBlock(Element htmlBlock) {
Manga mangaFromHtmlBlock = new Manga();
mangaFromHtmlBlock.source = getSourceId();
mangaFromHtmlBlock.source = getId();
Element urlElement = htmlBlock.select("div.title > a").first();
@ -153,7 +153,7 @@ public class Mangahere extends Source {
private Manga constructSearchMangaFromHtmlBlock(Element htmlBlock) {
Manga mangaFromHtmlBlock = new Manga();
mangaFromHtmlBlock.source = getSourceId();
mangaFromHtmlBlock.source = getId();
Element urlElement = htmlBlock.select("a.manga_info").first();

View File

@ -5,15 +5,14 @@ import android.app.Application;
import javax.inject.Singleton;
import dagger.Component;
import eu.kanade.mangafeed.data.mangasync.services.MyAnimeList;
import eu.kanade.mangafeed.data.sync.UpdateMangaSyncService;
import eu.kanade.mangafeed.data.download.DownloadService;
import eu.kanade.mangafeed.data.mangasync.services.MyAnimeList;
import eu.kanade.mangafeed.data.source.base.Source;
import eu.kanade.mangafeed.data.sync.LibraryUpdateService;
import eu.kanade.mangafeed.data.sync.UpdateMangaSyncService;
import eu.kanade.mangafeed.injection.module.AppModule;
import eu.kanade.mangafeed.injection.module.DataModule;
import eu.kanade.mangafeed.ui.catalogue.CataloguePresenter;
import eu.kanade.mangafeed.ui.catalogue.SourcePresenter;
import eu.kanade.mangafeed.ui.download.DownloadPresenter;
import eu.kanade.mangafeed.ui.library.LibraryPresenter;
import eu.kanade.mangafeed.ui.manga.MangaActivity;
@ -37,7 +36,6 @@ public interface AppComponent {
void inject(LibraryPresenter libraryPresenter);
void inject(MangaPresenter mangaPresenter);
void inject(SourcePresenter sourcePresenter);
void inject(CataloguePresenter cataloguePresenter);
void inject(MangaInfoPresenter mangaInfoPresenter);
void inject(ChaptersPresenter chaptersPresenter);

View File

@ -1,8 +1,10 @@
package eu.kanade.mangafeed.ui.catalogue;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.Menu;
@ -10,9 +12,11 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.Spinner;
import java.util.List;
import java.util.concurrent.TimeUnit;
@ -22,9 +26,12 @@ import butterknife.ButterKnife;
import butterknife.OnItemClick;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.database.models.Manga;
import eu.kanade.mangafeed.data.source.base.Source;
import eu.kanade.mangafeed.ui.base.fragment.BaseRxFragment;
import eu.kanade.mangafeed.ui.main.MainActivity;
import eu.kanade.mangafeed.ui.manga.MangaActivity;
import eu.kanade.mangafeed.util.PageBundle;
import eu.kanade.mangafeed.util.ToastUtil;
import eu.kanade.mangafeed.widget.EndlessScrollListener;
import icepick.Icepick;
import icepick.State;
@ -40,23 +47,20 @@ public class CatalogueFragment extends BaseRxFragment<CataloguePresenter> {
@Bind(R.id.progress) ProgressBar progress;
@Bind(R.id.progress_grid) ProgressBar progressGrid;
private Toolbar toolbar;
private Spinner spinner;
private CatalogueAdapter adapter;
private EndlessScrollListener scrollListener;
@State String query = "";
@State int selectedIndex = -1;
private final int SEARCH_TIMEOUT = 1000;
private PublishSubject<String> queryDebouncerSubject;
private Subscription queryDebouncerSubscription;
public final static String SOURCE_ID = "source_id";
public static CatalogueFragment newInstance(int sourceId) {
CatalogueFragment fragment = new CatalogueFragment();
Bundle args = new Bundle();
args.putInt(SOURCE_ID, sourceId);
fragment.setArguments(args);
return fragment;
public static CatalogueFragment newInstance() {
return new CatalogueFragment();
}
@Override
@ -78,13 +82,43 @@ public class CatalogueFragment extends BaseRxFragment<CataloguePresenter> {
gridView.setAdapter(adapter);
gridView.setOnScrollListener(scrollListener);
int sourceId = getArguments().getInt(SOURCE_ID, -1);
// Create toolbar spinner
Context themedContext = getBaseActivity().getSupportActionBar() != null ?
getBaseActivity().getSupportActionBar().getThemedContext() : getActivity();
spinner = new Spinner(themedContext);
CatalogueSpinnerAdapter spinnerAdapter = new CatalogueSpinnerAdapter(themedContext,
android.R.layout.simple_spinner_item, getPresenter().getEnabledSources());
spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(spinnerAdapter);
spinner.setSelection(savedState == null ? spinnerAdapter.getEmptyIndex() : selectedIndex);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
Source source = spinnerAdapter.getItem(position);
// We add an empty source with id -1 that acts as a placeholder to show a hint
// that asks to select a source
if (source.getId() != -1 && selectedIndex != position) {
// Set previous selection if it's not a valid source and notify the user
if (!getPresenter().isValidSource(source)) {
spinner.setSelection(selectedIndex != -1 ? selectedIndex :
spinnerAdapter.getEmptyIndex());
ToastUtil.showShort(getActivity(), R.string.source_requires_login);
} else {
selectedIndex = position;
showProgressBar();
getPresenter().startRequesting(source);
}
}
}
showProgressBar();
if (savedState == null)
getPresenter().startRequesting(sourceId);
@Override
public void onNothingSelected(AdapterView<?> parent) {}
});
setToolbarTitle("");
toolbar = ((MainActivity)getActivity()).getToolbar();
toolbar.addView(spinner);
setToolbarTitle(getPresenter().getSource().getName());
return view;
}
@ -128,6 +162,12 @@ public class CatalogueFragment extends BaseRxFragment<CataloguePresenter> {
super.onStop();
}
@Override
public void onDestroyView() {
toolbar.removeView(spinner);
super.onDestroyView();
}
@Override
public void onSaveInstanceState(Bundle outState) {
Icepick.saveInstanceState(this, outState);

View File

@ -13,6 +13,7 @@ import javax.inject.Inject;
import eu.kanade.mangafeed.data.cache.CoverCache;
import eu.kanade.mangafeed.data.database.DatabaseHelper;
import eu.kanade.mangafeed.data.database.models.Manga;
import eu.kanade.mangafeed.data.preference.PreferencesHelper;
import eu.kanade.mangafeed.data.source.SourceManager;
import eu.kanade.mangafeed.data.source.base.Source;
import eu.kanade.mangafeed.data.source.model.MangasPage;
@ -30,6 +31,7 @@ public class CataloguePresenter extends BasePresenter<CatalogueFragment> {
@Inject SourceManager sourceManager;
@Inject DatabaseHelper db;
@Inject CoverCache coverCache;
@Inject PreferencesHelper prefs;
private Source source;
@ -85,8 +87,8 @@ public class CataloguePresenter extends BasePresenter<CatalogueFragment> {
super.onDestroy();
}
public void startRequesting(int sourceId) {
source = sourceManager.get(sourceId);
public void startRequesting(Source source) {
this.source = source;
restartRequest(null);
}
@ -123,7 +125,7 @@ public class CataloguePresenter extends BasePresenter<CatalogueFragment> {
}
private Manga networkToLocalManga(Manga networkManga) {
List<Manga> dbResult = db.getManga(networkManga.url, source.getSourceId()).executeAsBlocking();
List<Manga> dbResult = db.getManga(networkManga.url, source.getId()).executeAsBlocking();
Manga localManga = !dbResult.isEmpty() ? dbResult.get(0) : null;
if (localManga == null) {
PutResult result = db.insertManga(networkManga).executeAsBlocking();
@ -152,4 +154,17 @@ public class CataloguePresenter extends BasePresenter<CatalogueFragment> {
return lastMangasPage != null && lastMangasPage.nextPageUrl != null;
}
public boolean isValidSource(Source source) {
if (!source.isLoginRequired() || source.isLogged())
return true;
return !(prefs.getSourceUsername(source).equals("")
|| prefs.getSourcePassword(source).equals(""));
}
public List<Source> getEnabledSources() {
// TODO filter by enabled source
return sourceManager.getSources();
}
}

View File

@ -0,0 +1,120 @@
package eu.kanade.mangafeed.ui.catalogue;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import org.jsoup.nodes.Document;
import java.util.List;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.database.models.Chapter;
import eu.kanade.mangafeed.data.database.models.Manga;
import eu.kanade.mangafeed.data.source.base.Source;
import eu.kanade.mangafeed.data.source.model.MangasPage;
public class CatalogueSpinnerAdapter extends ArrayAdapter<Source> {
public CatalogueSpinnerAdapter(Context context, int resource, List<Source> sources) {
super(context, resource, sources);
sources.add(new SimpleSource());
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = super.getView(position, convertView, parent);
if (position == getCount()) {
((TextView)v.findViewById(android.R.id.text1)).setText("");
((TextView)v.findViewById(android.R.id.text1)).setHint(getItem(getCount()).getName());
}
return v;
}
@Override
public int getCount() {
return super.getCount()-1; // you dont display last item. It is used as hint.
}
public int getEmptyIndex() {
return getCount();
}
private class SimpleSource extends Source {
@Override
public String getName() {
return getContext().getString(R.string.select_source);
}
@Override
public int getId() {
return -1;
}
@Override
public String getBaseUrl() {
return null;
}
@Override
public boolean isLoginRequired() {
return false;
}
@Override
protected String getInitialPopularMangasUrl() {
return null;
}
@Override
protected String getInitialSearchUrl(String query) {
return null;
}
@Override
protected List<Manga> parsePopularMangasFromHtml(Document parsedHtml) {
return null;
}
@Override
protected String parseNextPopularMangasUrl(Document parsedHtml, MangasPage page) {
return null;
}
@Override
protected List<Manga> parseSearchFromHtml(Document parsedHtml) {
return null;
}
@Override
protected String parseNextSearchUrl(Document parsedHtml, MangasPage page, String query) {
return null;
}
@Override
protected Manga parseHtmlToManga(String mangaUrl, String unparsedHtml) {
return null;
}
@Override
protected List<Chapter> parseHtmlToChapters(String unparsedHtml) {
return null;
}
@Override
protected List<String> parseHtmlToPageUrls(String unparsedHtml) {
return null;
}
@Override
protected String parseHtmlToImageUrl(String unparsedHtml) {
return null;
}
}
}

View File

@ -1,69 +0,0 @@
package eu.kanade.mangafeed.ui.catalogue;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import android.widget.Toast;
import java.util.List;
import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnItemClick;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.source.base.Source;
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;
@RequiresPresenter(SourcePresenter.class)
public class SourceFragment extends BaseRxFragment<SourcePresenter> {
@Bind(R.id.catalogue_list) ListView source_list;
private EasyAdapter<Source> adapter;
public static SourceFragment newInstance() {
return new SourceFragment();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_source, container, false);
ButterKnife.bind(this, view);
setToolbarTitle(R.string.label_catalogues);
createAdapter();
return view;
}
@OnItemClick(R.id.catalogue_list)
public void onSourceClick(int position) {
Source source = adapter.getItem(position);
MainActivity activity = (MainActivity) getActivity();
if (getPresenter().isValidSource(source)) {
CatalogueFragment fragment = CatalogueFragment.newInstance(source.getSourceId());
activity.setFragment(fragment);
} else {
Toast.makeText(getActivity(), R.string.source_requires_login, Toast.LENGTH_SHORT).show();
}
}
private void createAdapter() {
adapter = new EasyAdapter<>(getActivity(), SourceHolder.class);
source_list.setAdapter(adapter);
}
public void setItems(List<Source> items) {
adapter.setItems(items);
}
}

View File

@ -1,28 +0,0 @@
package eu.kanade.mangafeed.ui.catalogue;
import android.view.View;
import android.widget.TextView;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.source.base.Source;
import uk.co.ribot.easyadapter.ItemViewHolder;
import uk.co.ribot.easyadapter.PositionInfo;
import uk.co.ribot.easyadapter.annotations.LayoutId;
import uk.co.ribot.easyadapter.annotations.ViewId;
@LayoutId(R.layout.item_source)
public class SourceHolder extends ItemViewHolder<Source> {
@ViewId(R.id.source_name)
TextView source_name;
public SourceHolder(View view) {
super(view);
}
@Override
public void onSetValues(Source item, PositionInfo positionInfo) {
source_name.setText(item.getName());
}
}

View File

@ -1,30 +0,0 @@
package eu.kanade.mangafeed.ui.catalogue;
import javax.inject.Inject;
import eu.kanade.mangafeed.data.preference.PreferencesHelper;
import eu.kanade.mangafeed.data.source.SourceManager;
import eu.kanade.mangafeed.data.source.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(""));
}
}

View File

@ -14,7 +14,7 @@ import butterknife.Bind;
import butterknife.ButterKnife;
import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.ui.base.activity.BaseActivity;
import eu.kanade.mangafeed.ui.catalogue.SourceFragment;
import eu.kanade.mangafeed.ui.catalogue.CatalogueFragment;
import eu.kanade.mangafeed.ui.download.DownloadFragment;
import eu.kanade.mangafeed.ui.library.LibraryFragment;
import eu.kanade.mangafeed.ui.setting.SettingsActivity;
@ -80,7 +80,7 @@ public class MainActivity extends BaseActivity {
case R.id.nav_drawer_recent_updates:
break;
case R.id.nav_drawer_catalogues:
setFragment(SourceFragment.newInstance());
setFragment(CatalogueFragment.newInstance());
break;
case R.id.nav_drawer_downloads:
setFragment(DownloadFragment.newInstance());
@ -111,4 +111,8 @@ public class MainActivity extends BaseActivity {
fragmentStack.replace(fragment);
}
public Toolbar getToolbar() {
return toolbar;
}
}

View File

@ -128,7 +128,7 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
EventBus.getDefault().removeStickyEvent(event);
manga = event.getManga();
source = event.getSource();
sourceId = source.getSourceId();
sourceId = source.getId();
loadChapter(event.getChapter());
}

View File

@ -1,13 +0,0 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="eu.kanade.mangafeed.ui.catalogue.SourceFragment">
<ListView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/catalogue_list" />
</LinearLayout>

View File

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
tools:text="New Text"
android:id="@+id/source_name"
android:gravity="center_vertical"
android:paddingLeft="15dp"/>
</LinearLayout>

View File

@ -90,6 +90,7 @@
<!-- Catalogue fragment -->
<string name="source_requires_login">This source requires login</string>
<string name="select_source">Select a source</string>
<!-- Manga info fragment -->
<string name="manga_detail_tab">Info</string>