deleteChapterSync(ChapterSync chapter) {
+ return db.delete()
+ .object(chapter)
+ .prepare();
+ }
}
diff --git a/app/src/main/java/eu/kanade/mangafeed/data/database/DbOpenHelper.java b/app/src/main/java/eu/kanade/mangafeed/data/database/DbOpenHelper.java
index bfebf5ed8..95a2a9473 100644
--- a/app/src/main/java/eu/kanade/mangafeed/data/database/DbOpenHelper.java
+++ b/app/src/main/java/eu/kanade/mangafeed/data/database/DbOpenHelper.java
@@ -5,13 +5,14 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.support.annotation.NonNull;
+import eu.kanade.mangafeed.data.database.tables.ChapterSyncTable;
import eu.kanade.mangafeed.data.database.tables.ChapterTable;
import eu.kanade.mangafeed.data.database.tables.MangaTable;
public class DbOpenHelper extends SQLiteOpenHelper {
public static final String DATABASE_NAME = "mangafeed.db";
- public static final int DATABASE_VERSION = 1;
+ public static final int DATABASE_VERSION = 2;
public DbOpenHelper(@NonNull Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
@@ -21,11 +22,13 @@ public class DbOpenHelper extends SQLiteOpenHelper {
public void onCreate(@NonNull SQLiteDatabase db) {
db.execSQL(MangaTable.getCreateTableQuery());
db.execSQL(ChapterTable.getCreateTableQuery());
+ db.execSQL(ChapterSyncTable.getCreateTableQuery());
}
@Override
public void onUpgrade(@NonNull SQLiteDatabase db, int oldVersion, int newVersion) {
- // no impl
+ if (oldVersion == 1)
+ db.execSQL(ChapterSyncTable.getCreateTableQuery());
}
@Override
diff --git a/app/src/main/java/eu/kanade/mangafeed/data/database/models/ChapterSync.java b/app/src/main/java/eu/kanade/mangafeed/data/database/models/ChapterSync.java
new file mode 100644
index 000000000..7a0897bfb
--- /dev/null
+++ b/app/src/main/java/eu/kanade/mangafeed/data/database/models/ChapterSync.java
@@ -0,0 +1,35 @@
+package eu.kanade.mangafeed.data.database.models;
+
+import com.pushtorefresh.storio.sqlite.annotations.StorIOSQLiteColumn;
+import com.pushtorefresh.storio.sqlite.annotations.StorIOSQLiteType;
+
+import eu.kanade.mangafeed.data.chaptersync.BaseChapterSync;
+import eu.kanade.mangafeed.data.database.tables.ChapterSyncTable;
+
+@StorIOSQLiteType(table = ChapterSyncTable.TABLE)
+public class ChapterSync {
+
+ @StorIOSQLiteColumn(name = ChapterSyncTable.COLUMN_ID, key = true)
+ public long id;
+
+ @StorIOSQLiteColumn(name = ChapterSyncTable.COLUMN_MANGA_ID)
+ public long manga_id;
+
+ @StorIOSQLiteColumn(name = ChapterSyncTable.COLUMN_SYNC_ID)
+ public int sync_id;
+
+ @StorIOSQLiteColumn(name = ChapterSyncTable.COLUMN_REMOTE_ID)
+ public int remote_id;
+
+ @StorIOSQLiteColumn(name = ChapterSyncTable.COLUMN_TITLE)
+ public String title;
+
+ @StorIOSQLiteColumn(name = ChapterSyncTable.COLUMN_LAST_CHAPTER_READ)
+ public int last_chapter_read;
+
+ public static ChapterSync create(BaseChapterSync sync) {
+ ChapterSync chapter = new ChapterSync();
+ chapter.sync_id = sync.getId();
+ return chapter;
+ }
+}
diff --git a/app/src/main/java/eu/kanade/mangafeed/data/database/tables/ChapterSyncTable.java b/app/src/main/java/eu/kanade/mangafeed/data/database/tables/ChapterSyncTable.java
new file mode 100644
index 000000000..98bb75501
--- /dev/null
+++ b/app/src/main/java/eu/kanade/mangafeed/data/database/tables/ChapterSyncTable.java
@@ -0,0 +1,35 @@
+package eu.kanade.mangafeed.data.database.tables;
+
+import android.support.annotation.NonNull;
+
+public class ChapterSyncTable {
+
+ public static final String TABLE = "chapter_sync";
+
+ public static final String COLUMN_ID = "_id";
+
+ public static final String COLUMN_MANGA_ID = "manga_id";
+
+ public static final String COLUMN_SYNC_ID = "sync_id";
+
+ public static final String COLUMN_REMOTE_ID = "remote_id";
+
+ public static final String COLUMN_TITLE = "title";
+
+ public static final String COLUMN_LAST_CHAPTER_READ = "last_chapter_read";
+
+ @NonNull
+ public static String getCreateTableQuery() {
+ return "CREATE TABLE " + TABLE + "("
+ + COLUMN_ID + " INTEGER NOT NULL PRIMARY KEY, "
+ + COLUMN_MANGA_ID + " INTEGER NOT NULL, "
+ + COLUMN_SYNC_ID + " INTEGER NOT NULL, "
+ + COLUMN_REMOTE_ID + " INTEGER NOT NULL, "
+ + COLUMN_TITLE + " TEXT NOT NULL, "
+ + COLUMN_LAST_CHAPTER_READ + " INTEGER NOT NULL, "
+ + "FOREIGN KEY(" + COLUMN_MANGA_ID + ") REFERENCES " + MangaTable.TABLE + "(" + MangaTable.COLUMN_ID + ") "
+ + "ON DELETE CASCADE"
+ + ");";
+ }
+
+}
diff --git a/app/src/main/java/eu/kanade/mangafeed/data/preference/PreferencesHelper.java b/app/src/main/java/eu/kanade/mangafeed/data/preference/PreferencesHelper.java
index 8197163f4..8d0a4b5c9 100644
--- a/app/src/main/java/eu/kanade/mangafeed/data/preference/PreferencesHelper.java
+++ b/app/src/main/java/eu/kanade/mangafeed/data/preference/PreferencesHelper.java
@@ -8,6 +8,7 @@ import com.f2prateek.rx.preferences.Preference;
import com.f2prateek.rx.preferences.RxSharedPreferences;
import eu.kanade.mangafeed.R;
+import eu.kanade.mangafeed.data.chaptersync.BaseChapterSync;
import eu.kanade.mangafeed.data.source.base.Source;
import eu.kanade.mangafeed.util.DiskUtils;
import rx.Observable;
@@ -20,6 +21,8 @@ public class PreferencesHelper {
private static final String SOURCE_ACCOUNT_USERNAME = "pref_source_username_";
private static final String SOURCE_ACCOUNT_PASSWORD = "pref_source_password_";
+ private static final String CHAPTERSYNC_ACCOUNT_USERNAME = "pref_chaptersync_username_";
+ private static final String CHAPTERSYNC_ACCOUNT_PASSWORD = "pref_chaptersync_password_";
public PreferencesHelper(Context context) {
this.context = context;
@@ -84,6 +87,21 @@ public class PreferencesHelper {
.apply();
}
+ public String getChapterSyncUsername(BaseChapterSync sync) {
+ return prefs.getString(CHAPTERSYNC_ACCOUNT_USERNAME + sync.getId(), "");
+ }
+
+ public String getChapterSyncPassword(BaseChapterSync sync) {
+ return prefs.getString(CHAPTERSYNC_ACCOUNT_PASSWORD + sync.getId(), "");
+ }
+
+ public void setChapterSyncCredentials(BaseChapterSync sync, String username, String password) {
+ prefs.edit()
+ .putString(CHAPTERSYNC_ACCOUNT_USERNAME + sync.getId(), username)
+ .putString(CHAPTERSYNC_ACCOUNT_PASSWORD + sync.getId(), password)
+ .apply();
+ }
+
public String getDownloadsDirectory() {
return prefs.getString(getKey(R.string.pref_download_directory_key),
DiskUtils.getStorageDirectories(context)[0]);
diff --git a/app/src/main/java/eu/kanade/mangafeed/event/UpdateChapterSyncEvent.java b/app/src/main/java/eu/kanade/mangafeed/event/UpdateChapterSyncEvent.java
new file mode 100644
index 000000000..965e10bb1
--- /dev/null
+++ b/app/src/main/java/eu/kanade/mangafeed/event/UpdateChapterSyncEvent.java
@@ -0,0 +1,17 @@
+package eu.kanade.mangafeed.event;
+
+import eu.kanade.mangafeed.data.database.models.ChapterSync;
+
+public class UpdateChapterSyncEvent {
+
+ private ChapterSync chapterSync;
+
+ public UpdateChapterSyncEvent(ChapterSync chapterSync) {
+ this.chapterSync = chapterSync;
+ }
+
+ public ChapterSync getChapterSync() {
+ return chapterSync;
+ }
+
+}
diff --git a/app/src/main/java/eu/kanade/mangafeed/injection/component/AppComponent.java b/app/src/main/java/eu/kanade/mangafeed/injection/component/AppComponent.java
index 893c4cfbc..2b81f4f77 100644
--- a/app/src/main/java/eu/kanade/mangafeed/injection/component/AppComponent.java
+++ b/app/src/main/java/eu/kanade/mangafeed/injection/component/AppComponent.java
@@ -5,6 +5,8 @@ import android.app.Application;
import javax.inject.Singleton;
import dagger.Component;
+import eu.kanade.mangafeed.data.chaptersync.MyAnimeList;
+import eu.kanade.mangafeed.data.chaptersync.UpdateChapterSyncService;
import eu.kanade.mangafeed.data.download.DownloadService;
import eu.kanade.mangafeed.data.sync.LibraryUpdateService;
import eu.kanade.mangafeed.injection.module.AppModule;
@@ -12,9 +14,11 @@ import eu.kanade.mangafeed.injection.module.DataModule;
import eu.kanade.mangafeed.ui.catalogue.CataloguePresenter;
import eu.kanade.mangafeed.ui.download.DownloadPresenter;
import eu.kanade.mangafeed.ui.library.LibraryPresenter;
+import eu.kanade.mangafeed.ui.manga.MangaActivity;
import eu.kanade.mangafeed.ui.manga.MangaPresenter;
import eu.kanade.mangafeed.ui.manga.chapter.ChaptersPresenter;
import eu.kanade.mangafeed.ui.manga.info.MangaInfoPresenter;
+import eu.kanade.mangafeed.ui.manga.myanimelist.MyAnimeListPresenter;
import eu.kanade.mangafeed.ui.reader.ReaderPresenter;
import eu.kanade.mangafeed.ui.catalogue.SourcePresenter;
import eu.kanade.mangafeed.data.source.base.Source;
@@ -39,15 +43,20 @@ public interface AppComponent {
void inject(ChaptersPresenter chaptersPresenter);
void inject(ReaderPresenter readerPresenter);
void inject(DownloadPresenter downloadPresenter);
+ void inject(MyAnimeListPresenter myAnimeListPresenter);
void inject(ReaderActivity readerActivity);
+ void inject(MangaActivity mangaActivity);
void inject(SettingsAccountsFragment settingsAccountsFragment);
void inject(SettingsDownloadsFragment settingsDownloadsFragment);
void inject(Source source);
+ void inject(MyAnimeList myAnimeList);
+
void inject(LibraryUpdateService libraryUpdateService);
void inject(DownloadService downloadService);
+ void inject(UpdateChapterSyncService updateChapterSyncService);
Application application();
diff --git a/app/src/main/java/eu/kanade/mangafeed/injection/module/DataModule.java b/app/src/main/java/eu/kanade/mangafeed/injection/module/DataModule.java
index a02d567c7..a373c0870 100644
--- a/app/src/main/java/eu/kanade/mangafeed/injection/module/DataModule.java
+++ b/app/src/main/java/eu/kanade/mangafeed/injection/module/DataModule.java
@@ -7,6 +7,7 @@ import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import eu.kanade.mangafeed.data.cache.CacheManager;
+import eu.kanade.mangafeed.data.chaptersync.ChapterSyncManager;
import eu.kanade.mangafeed.data.database.DatabaseHelper;
import eu.kanade.mangafeed.data.download.DownloadManager;
import eu.kanade.mangafeed.data.network.NetworkHelper;
@@ -56,4 +57,10 @@ public class DataModule {
return new DownloadManager(app, sourceManager, preferences);
}
+ @Provides
+ @Singleton
+ ChapterSyncManager provideChapterSyncManager(Application app) {
+ return new ChapterSyncManager(app);
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/eu/kanade/mangafeed/ui/base/activity/BaseRxActivity.java b/app/src/main/java/eu/kanade/mangafeed/ui/base/activity/BaseRxActivity.java
index bcc1aa063..cfab9a313 100644
--- a/app/src/main/java/eu/kanade/mangafeed/ui/base/activity/BaseRxActivity.java
+++ b/app/src/main/java/eu/kanade/mangafeed/ui/base/activity/BaseRxActivity.java
@@ -4,7 +4,7 @@ import android.os.Bundle;
import android.support.annotation.NonNull;
import eu.kanade.mangafeed.App;
-import eu.kanade.mangafeed.ui.base.activity.BaseActivity;
+import eu.kanade.mangafeed.ui.base.presenter.BasePresenter;
import nucleus.factory.PresenterFactory;
import nucleus.factory.ReflectionPresenterFactory;
import nucleus.presenter.Presenter;
@@ -60,7 +60,9 @@ public abstract class BaseRxActivity extends BaseActivity i
final PresenterFactory
superFactory = getPresenterFactory();
setPresenterFactory(() -> {
P presenter = superFactory.createPresenter();
- ((App)getApplication()).getComponentReflection().inject(presenter);
+ App app = (App) getApplication();
+ app.getComponentReflection().inject(presenter);
+ ((BasePresenter)presenter).setContext(app.getApplicationContext());
return presenter;
});
diff --git a/app/src/main/java/eu/kanade/mangafeed/ui/base/fragment/BaseRxFragment.java b/app/src/main/java/eu/kanade/mangafeed/ui/base/fragment/BaseRxFragment.java
index 15aaf34c1..f8e811198 100644
--- a/app/src/main/java/eu/kanade/mangafeed/ui/base/fragment/BaseRxFragment.java
+++ b/app/src/main/java/eu/kanade/mangafeed/ui/base/fragment/BaseRxFragment.java
@@ -3,7 +3,7 @@ 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 eu.kanade.mangafeed.ui.base.presenter.BasePresenter;
import nucleus.factory.PresenterFactory;
import nucleus.factory.ReflectionPresenterFactory;
import nucleus.presenter.Presenter;
@@ -57,7 +57,9 @@ public abstract class BaseRxFragment
extends BaseFragment i
final PresenterFactory
superFactory = getPresenterFactory();
setPresenterFactory(() -> {
P presenter = superFactory.createPresenter();
- ((App)getActivity().getApplication()).getComponentReflection().inject(presenter);
+ App app = (App) getActivity().getApplication();
+ app.getComponentReflection().inject(presenter);
+ ((BasePresenter)presenter).setContext(app.getApplicationContext());
return presenter;
});
diff --git a/app/src/main/java/eu/kanade/mangafeed/ui/base/presenter/BasePresenter.java b/app/src/main/java/eu/kanade/mangafeed/ui/base/presenter/BasePresenter.java
index 3023a7089..c03a7703c 100644
--- a/app/src/main/java/eu/kanade/mangafeed/ui/base/presenter/BasePresenter.java
+++ b/app/src/main/java/eu/kanade/mangafeed/ui/base/presenter/BasePresenter.java
@@ -1,5 +1,6 @@
package eu.kanade.mangafeed.ui.base.presenter;
+import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
@@ -10,6 +11,8 @@ import nucleus.view.ViewWithPresenter;
public class BasePresenter extends RxPresenter {
+ private Context context;
+
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
@@ -33,4 +36,13 @@ public class BasePresenter extends RxPresenter {
public void unregisterForEvents() {
EventBus.getDefault().unregister(this);
}
+
+ public void setContext(Context applicationContext) {
+ context = applicationContext;
+ }
+
+ public Context getContext() {
+ return context;
+ }
+
}
diff --git a/app/src/main/java/eu/kanade/mangafeed/ui/manga/MangaActivity.java b/app/src/main/java/eu/kanade/mangafeed/ui/manga/MangaActivity.java
index 612a48eed..3036134e8 100644
--- a/app/src/main/java/eu/kanade/mangafeed/ui/manga/MangaActivity.java
+++ b/app/src/main/java/eu/kanade/mangafeed/ui/manga/MangaActivity.java
@@ -11,13 +11,19 @@ import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.widget.Toolbar;
+import javax.inject.Inject;
+
import butterknife.Bind;
import butterknife.ButterKnife;
+import eu.kanade.mangafeed.App;
import eu.kanade.mangafeed.R;
+import eu.kanade.mangafeed.data.chaptersync.ChapterSyncManager;
import eu.kanade.mangafeed.data.database.models.Manga;
+import eu.kanade.mangafeed.data.preference.PreferencesHelper;
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 eu.kanade.mangafeed.ui.manga.myanimelist.MyAnimeListFragment;
import nucleus.factory.RequiresPresenter;
@RequiresPresenter(MangaPresenter.class)
@@ -27,6 +33,9 @@ public class MangaActivity extends BaseRxActivity {
@Bind(R.id.tabs) TabLayout tabs;
@Bind(R.id.view_pager) ViewPager view_pager;
+ @Inject PreferencesHelper preferences;
+ @Inject ChapterSyncManager chapterSyncManager;
+
private MangaDetailAdapter adapter;
private long manga_id;
private boolean is_online;
@@ -43,6 +52,7 @@ public class MangaActivity extends BaseRxActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ App.get(this).getComponent().inject(this);
setContentView(R.layout.activity_manga_detail);
ButterKnife.bind(this);
@@ -88,25 +98,31 @@ public class MangaActivity extends BaseRxActivity {
class MangaDetailAdapter extends FragmentPagerAdapter {
- final int PAGE_COUNT = 2;
- private String tab_titles[];
+ private int pageCount;
+ private String tabTitles[];
private Context context;
final static int INFO_FRAGMENT = 0;
final static int CHAPTERS_FRAGMENT = 1;
+ final static int MYANIMELIST_FRAGMENT = 2;
public MangaDetailAdapter(FragmentManager fm, Context context) {
super(fm);
this.context = context;
- tab_titles = new String[]{
+ tabTitles = new String[]{
context.getString(R.string.manga_detail_tab),
- context.getString(R.string.manga_chapters_tab)
+ context.getString(R.string.manga_chapters_tab),
+ "MAL"
};
+
+ pageCount = 2;
+ if (chapterSyncManager.getMyAnimeList().isLogged())
+ pageCount++;
}
@Override
public int getCount() {
- return PAGE_COUNT;
+ return pageCount;
}
@Override
@@ -116,7 +132,8 @@ public class MangaActivity extends BaseRxActivity {
return MangaInfoFragment.newInstance();
case CHAPTERS_FRAGMENT:
return ChaptersFragment.newInstance();
-
+ case MYANIMELIST_FRAGMENT:
+ return MyAnimeListFragment.newInstance();
default:
return null;
}
@@ -125,7 +142,7 @@ public class MangaActivity extends BaseRxActivity {
@Override
public CharSequence getPageTitle(int position) {
// Generate title based on item position
- return tab_titles[position];
+ return tabTitles[position];
}
}
diff --git a/app/src/main/java/eu/kanade/mangafeed/ui/manga/info/MangaInfoFragment.java b/app/src/main/java/eu/kanade/mangafeed/ui/manga/info/MangaInfoFragment.java
index e73f35c0a..ffcd818af 100644
--- a/app/src/main/java/eu/kanade/mangafeed/ui/manga/info/MangaInfoFragment.java
+++ b/app/src/main/java/eu/kanade/mangafeed/ui/manga/info/MangaInfoFragment.java
@@ -2,10 +2,6 @@ package eu.kanade.mangafeed.ui.manga.info;
import android.os.Bundle;
import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
@@ -52,28 +48,12 @@ public class MangaInfoFragment extends BaseRxFragment {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_manga_info, container, false);
ButterKnife.bind(this, view);
- favoriteBtn.setOnTouchListener((v, event) -> {
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
- getPresenter().toggleFavorite();
- return true;
- }
- return false;
+ favoriteBtn.setOnClickListener(v -> {
+ getPresenter().toggleFavorite();
});
- getPresenter().initFavoriteText();
+
return view;
-
-
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- super.onCreateOptionsMenu(menu, inflater);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- return super.onOptionsItemSelected(item);
}
public void setMangaInfo(Manga manga) {
diff --git a/app/src/main/java/eu/kanade/mangafeed/ui/manga/myanimelist/MyAnimeListDialogFragment.java b/app/src/main/java/eu/kanade/mangafeed/ui/manga/myanimelist/MyAnimeListDialogFragment.java
new file mode 100644
index 000000000..206398d8a
--- /dev/null
+++ b/app/src/main/java/eu/kanade/mangafeed/ui/manga/myanimelist/MyAnimeListDialogFragment.java
@@ -0,0 +1,99 @@
+package eu.kanade.mangafeed.ui.manga.myanimelist;
+
+import android.app.Dialog;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+import android.support.v7.app.AlertDialog;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.List;
+
+import butterknife.Bind;
+import butterknife.ButterKnife;
+import eu.kanade.mangafeed.R;
+import eu.kanade.mangafeed.data.database.models.ChapterSync;
+import uk.co.ribot.easyadapter.EasyAdapter;
+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;
+
+public class MyAnimeListDialogFragment extends DialogFragment {
+
+ @Bind(R.id.myanimelist_search_field) EditText searchText;
+ @Bind(R.id.myanimelist_search_button) Button searchButton;
+ @Bind(R.id.myanimelist_search_results) ListView searchResults;
+
+ private EasyAdapter adapter;
+ private MyAnimeListFragment fragment;
+ private ChapterSync selectedItem;
+
+ public static MyAnimeListDialogFragment newInstance(MyAnimeListFragment parentFragment) {
+ MyAnimeListDialogFragment dialog = new MyAnimeListDialogFragment();
+ dialog.setParentFragment(parentFragment);
+ return dialog;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedState) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+
+ LayoutInflater inflater = getActivity().getLayoutInflater();
+
+ View view = inflater.inflate(R.layout.dialog_myanimelist_search, null);
+ ButterKnife.bind(this, view);
+
+
+ builder.setView(view)
+ .setPositiveButton(R.string.button_ok, (dialog, which) -> onPositiveButtonClick())
+ .setNegativeButton(R.string.button_cancel, (dialog, which) -> {});
+
+ searchButton.setOnClickListener(v ->
+ fragment.getPresenter().searchManga(searchText.getText().toString()));
+
+ searchResults.setOnItemClickListener((parent, viewList, position, id) ->
+ selectedItem = adapter.getItem(position));
+
+ adapter = new EasyAdapter<>(getActivity(), ResultViewHolder.class);
+
+ searchResults.setAdapter(adapter);
+
+ return builder.create();
+ }
+
+ private void onPositiveButtonClick() {
+ if (adapter != null && selectedItem != null) {
+ fragment.getPresenter().registerManga(selectedItem);
+ }
+ }
+
+ public void setResults(List results) {
+ selectedItem = null;
+ adapter.setItems(results);
+ }
+
+ public void setParentFragment(MyAnimeListFragment fragment) {
+ this.fragment = fragment;
+ }
+
+ @LayoutId(R.layout.dialog_myanimelist_search_item)
+ public static class ResultViewHolder extends ItemViewHolder {
+
+ @ViewId(R.id.myanimelist_result_title) TextView title;
+
+ public ResultViewHolder(View view) {
+ super(view);
+ }
+
+ @Override
+ public void onSetValues(ChapterSync chapter, PositionInfo positionInfo) {
+ title.setText(chapter.title);
+ }
+ }
+
+}
diff --git a/app/src/main/java/eu/kanade/mangafeed/ui/manga/myanimelist/MyAnimeListFragment.java b/app/src/main/java/eu/kanade/mangafeed/ui/manga/myanimelist/MyAnimeListFragment.java
new file mode 100644
index 000000000..c2ec9b042
--- /dev/null
+++ b/app/src/main/java/eu/kanade/mangafeed/ui/manga/myanimelist/MyAnimeListFragment.java
@@ -0,0 +1,85 @@
+package eu.kanade.mangafeed.ui.manga.myanimelist;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import java.util.List;
+
+import butterknife.Bind;
+import butterknife.ButterKnife;
+import eu.kanade.mangafeed.R;
+import eu.kanade.mangafeed.data.database.models.ChapterSync;
+import eu.kanade.mangafeed.ui.base.fragment.BaseRxFragment;
+import nucleus.factory.RequiresPresenter;
+
+@RequiresPresenter(MyAnimeListPresenter.class)
+public class MyAnimeListFragment extends BaseRxFragment {
+
+ @Bind(R.id.myanimelist_title) TextView title;
+ @Bind(R.id.myanimelist_last_chapter_read) EditText lastChapterRead;
+ @Bind(R.id.update_button) Button updateButton;
+
+ private MyAnimeListDialogFragment dialog;
+
+ public static MyAnimeListFragment newInstance() {
+ return new MyAnimeListFragment();
+ }
+
+ @Override
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_myanimelist, container, false);
+ ButterKnife.bind(this, view);
+
+ updateButton.setOnClickListener(v -> getPresenter().updateLastChapter(
+ Integer.parseInt(lastChapterRead.getText().toString())));
+
+ return view;
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ inflater.inflate(R.menu.myanimelist, menu);
+ super.onCreateOptionsMenu(menu, inflater);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.myanimelist_edit:
+ showSearchDialog();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ public void setChapterSync(ChapterSync chapterSync) {
+ title.setText(chapterSync.title);
+ lastChapterRead.setText(chapterSync.last_chapter_read + "");
+ }
+
+ private void showSearchDialog() {
+ if (dialog == null)
+ dialog = MyAnimeListDialogFragment.newInstance(this);
+
+ dialog.show(getActivity().getSupportFragmentManager(), "search");
+ }
+
+ public void onSearchResults(List results) {
+ if (dialog != null)
+ dialog.setResults(results);
+ }
+}
diff --git a/app/src/main/java/eu/kanade/mangafeed/ui/manga/myanimelist/MyAnimeListPresenter.java b/app/src/main/java/eu/kanade/mangafeed/ui/manga/myanimelist/MyAnimeListPresenter.java
new file mode 100644
index 000000000..d19d4c487
--- /dev/null
+++ b/app/src/main/java/eu/kanade/mangafeed/ui/manga/myanimelist/MyAnimeListPresenter.java
@@ -0,0 +1,105 @@
+package eu.kanade.mangafeed.ui.manga.myanimelist;
+
+import android.os.Bundle;
+
+import javax.inject.Inject;
+
+import eu.kanade.mangafeed.data.chaptersync.ChapterSyncManager;
+import eu.kanade.mangafeed.data.chaptersync.MyAnimeList;
+import eu.kanade.mangafeed.data.database.DatabaseHelper;
+import eu.kanade.mangafeed.data.database.models.ChapterSync;
+import eu.kanade.mangafeed.data.database.models.Manga;
+import eu.kanade.mangafeed.ui.base.presenter.BasePresenter;
+import eu.kanade.mangafeed.util.EventBusHook;
+import rx.Observable;
+import rx.Subscription;
+import rx.android.schedulers.AndroidSchedulers;
+import rx.schedulers.Schedulers;
+import timber.log.Timber;
+
+public class MyAnimeListPresenter extends BasePresenter {
+
+ @Inject DatabaseHelper db;
+ @Inject ChapterSyncManager syncManager;
+
+ private MyAnimeList myAnimeList;
+ private Manga manga;
+ private ChapterSync chapterSync;
+
+ private String query;
+
+ private Subscription updateSubscription;
+
+ private static final int GET_CHAPTER_SYNC = 1;
+ private static final int GET_SEARCH_RESULTS = 2;
+
+ @Override
+ protected void onCreate(Bundle savedState) {
+ super.onCreate(savedState);
+
+ myAnimeList = syncManager.getMyAnimeList();
+
+ restartableLatestCache(GET_CHAPTER_SYNC,
+ () -> db.getChapterSync(manga, myAnimeList).createObservable()
+ .flatMap(Observable::from)
+ .doOnNext(chapterSync -> this.chapterSync = chapterSync)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread()),
+ MyAnimeListFragment::setChapterSync);
+
+ restartableLatestCache(GET_SEARCH_RESULTS,
+ () -> myAnimeList.search(query)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread()),
+ (view, results) -> {
+ view.onSearchResults(results);
+ }, (view, error) -> {
+ Timber.e(error.getMessage());
+ });
+
+ }
+
+ @Override
+ protected void onTakeView(MyAnimeListFragment view) {
+ super.onTakeView(view);
+ registerForStickyEvents();
+ }
+
+ @Override
+ protected void onDropView() {
+ unregisterForEvents();
+ super.onDropView();
+ }
+
+ @EventBusHook
+ public void onEventMainThread(Manga manga) {
+ this.manga = manga;
+ start(GET_CHAPTER_SYNC);
+ }
+
+ public void updateLastChapter(int chapterNumber) {
+ if (updateSubscription != null)
+ remove(updateSubscription);
+
+ chapterSync.last_chapter_read = chapterNumber;
+
+ add(updateSubscription = myAnimeList.update(chapterSync)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(response -> {},
+ error -> {
+ Timber.e(error.getMessage());
+ }
+ ));
+ }
+
+ public void searchManga(String query) {
+ this.query = query;
+ start(GET_SEARCH_RESULTS);
+ }
+
+ public void registerManga(ChapterSync selectedManga) {
+ selectedManga.manga_id = manga.id;
+ db.insertChapterSync(selectedManga).executeAsBlocking();
+ }
+}
diff --git a/app/src/main/java/eu/kanade/mangafeed/ui/reader/ReaderPresenter.java b/app/src/main/java/eu/kanade/mangafeed/ui/reader/ReaderPresenter.java
index 37e605d91..f99cc28f6 100644
--- a/app/src/main/java/eu/kanade/mangafeed/ui/reader/ReaderPresenter.java
+++ b/app/src/main/java/eu/kanade/mangafeed/ui/reader/ReaderPresenter.java
@@ -8,8 +8,12 @@ import java.util.List;
import javax.inject.Inject;
import de.greenrobot.event.EventBus;
+import eu.kanade.mangafeed.data.chaptersync.ChapterSyncManager;
+import eu.kanade.mangafeed.data.chaptersync.MyAnimeList;
+import eu.kanade.mangafeed.data.chaptersync.UpdateChapterSyncService;
import eu.kanade.mangafeed.data.database.DatabaseHelper;
import eu.kanade.mangafeed.data.database.models.Chapter;
+import eu.kanade.mangafeed.data.database.models.ChapterSync;
import eu.kanade.mangafeed.data.database.models.Manga;
import eu.kanade.mangafeed.data.download.DownloadManager;
import eu.kanade.mangafeed.data.preference.PreferencesHelper;
@@ -17,6 +21,7 @@ import eu.kanade.mangafeed.data.source.base.Source;
import eu.kanade.mangafeed.data.source.model.Page;
import eu.kanade.mangafeed.event.RetryPageEvent;
import eu.kanade.mangafeed.event.SourceMangaChapterEvent;
+import eu.kanade.mangafeed.event.UpdateChapterSyncEvent;
import eu.kanade.mangafeed.ui.base.presenter.BasePresenter;
import eu.kanade.mangafeed.util.EventBusHook;
import icepick.State;
@@ -32,6 +37,7 @@ public class ReaderPresenter extends BasePresenter {
@Inject PreferencesHelper prefs;
@Inject DatabaseHelper db;
@Inject DownloadManager downloadManager;
+ @Inject ChapterSyncManager syncManager;
private Source source;
private Manga manga;
@@ -135,10 +141,47 @@ public class ReaderPresenter extends BasePresenter {
}
private void onChapterChange() {
- if (pageList != null) {
- if (!isDownloaded)
- source.savePageList(chapter.url, pageList);
- saveChapterProgress();
+ if (pageList == null)
+ return;
+
+ // Cache page list for online chapters to allow a faster reopen
+ if (!isDownloaded)
+ source.savePageList(chapter.url, pageList);
+
+ // Save current progress of the chapter. Mark as read if the chapter is finished
+ // and update progress in remote services (like MyAnimeList)
+ chapter.last_page_read = currentPage;
+ if (isChapterFinished()) {
+ chapter.read = true;
+ updateChapterSyncLastChapterRead();
+ }
+ db.insertChapter(chapter).executeAsBlocking();
+ }
+
+ private boolean isChapterFinished() {
+ return !chapter.read && currentPage == pageList.size() - 1;
+ }
+
+ private void updateChapterSyncLastChapterRead() {
+ // TODO don't use MAL methods for possible alternatives to MAL
+ MyAnimeList mal = syncManager.getMyAnimeList();
+
+ if (!mal.isLogged())
+ return;
+
+ List result = db.getChapterSync(manga, mal).executeAsBlocking();
+ if (result.isEmpty())
+ return;
+
+ ChapterSync chapterSync = result.get(0);
+
+ int lastChapterReadLocal = (int) Math.floor(chapter.chapter_number);
+ int lastChapterReadRemote = chapterSync.last_chapter_read;
+
+ if (lastChapterReadLocal > lastChapterReadRemote) {
+ chapterSync.last_chapter_read = lastChapterReadLocal;
+ EventBus.getDefault().postSticky(new UpdateChapterSyncEvent(chapterSync));
+ UpdateChapterSyncService.start(getContext());
}
}
@@ -186,14 +229,6 @@ public class ReaderPresenter extends BasePresenter {
this.currentPage = currentPage;
}
- private void saveChapterProgress() {
- chapter.last_page_read = currentPage;
- if (currentPage == pageList.size() - 1) {
- chapter.read = true;
- }
- db.insertChapter(chapter).executeAsBlocking();
- }
-
private void getAdjacentChapters() {
if (nextChapterSubscription != null)
remove(nextChapterSubscription);
diff --git a/app/src/main/java/eu/kanade/mangafeed/ui/setting/LoginDialogPreference.java b/app/src/main/java/eu/kanade/mangafeed/ui/setting/LoginDialogPreference.java
deleted file mode 100644
index dfbfc07ff..000000000
--- a/app/src/main/java/eu/kanade/mangafeed/ui/setting/LoginDialogPreference.java
+++ /dev/null
@@ -1,124 +0,0 @@
-package eu.kanade.mangafeed.ui.setting;
-
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.preference.DialogPreference;
-import android.text.method.PasswordTransformationMethod;
-import android.view.View;
-import android.widget.CheckBox;
-import android.widget.EditText;
-import android.widget.TextView;
-
-import com.dd.processbutton.iml.ActionProcessButton;
-
-import butterknife.Bind;
-import butterknife.ButterKnife;
-import eu.kanade.mangafeed.R;
-import eu.kanade.mangafeed.data.preference.PreferencesHelper;
-import eu.kanade.mangafeed.data.source.base.Source;
-import eu.kanade.mangafeed.util.ToastUtil;
-import rx.Subscription;
-import rx.android.schedulers.AndroidSchedulers;
-import rx.schedulers.Schedulers;
-
-public class LoginDialogPreference extends DialogPreference {
-
- @Bind(R.id.accounts_login) TextView title;
- @Bind(R.id.username) EditText username;
- @Bind(R.id.password) EditText password;
- @Bind(R.id.show_password) CheckBox showPassword;
- @Bind(R.id.login) ActionProcessButton loginBtn;
-
- private PreferencesHelper preferences;
- private Source source;
- private AlertDialog dialog;
- private Subscription requestSubscription;
- private Context context;
-
- public LoginDialogPreference(Context context, PreferencesHelper preferences, Source source) {
- super(context, null);
- this.context = context;
- this.preferences = preferences;
- this.source = source;
-
- setDialogLayoutResource(R.layout.pref_account_login);
- }
-
- @Override
- protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
- // Hide positive button
- builder.setPositiveButton("", this);
- }
-
- @Override
- protected void onBindDialogView(View view) {
- ButterKnife.bind(this, view);
-
- title.setText(getContext().getString(R.string.accounts_login_title, source.getName()));
-
- username.setText(preferences.getSourceUsername(source));
- password.setText(preferences.getSourcePassword(source));
- showPassword.setOnCheckedChangeListener((buttonView, isChecked) -> {
- if (isChecked)
- password.setTransformationMethod(null);
- else
- password.setTransformationMethod(new PasswordTransformationMethod());
- });
-
- loginBtn.setMode(ActionProcessButton.Mode.ENDLESS);
- loginBtn.setOnClickListener(click -> checkLogin());
-
- super.onBindDialogView(view);
- }
-
- @Override
- public void showDialog(Bundle state) {
- super.showDialog(state);
- dialog = ((AlertDialog) getDialog());
- }
-
- @Override
- protected void onDialogClosed(boolean positiveResult) {
- if (requestSubscription != null)
- requestSubscription.unsubscribe();
-
- if(!positiveResult)
- return;
-
- preferences.setSourceCredentials(source,
- username.getText().toString(),
- password.getText().toString());
- }
-
- private void checkLogin() {
- if (requestSubscription != null)
- requestSubscription.unsubscribe();
-
- if (username.getText().length() == 0 || password.getText().length() == 0)
- return;
-
- loginBtn.setProgress(1);
-
- requestSubscription = source
- .login(username.getText().toString(), password.getText().toString())
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(logged -> {
- if (logged) {
- // Simulate a positive button click and dismiss the dialog
- onClick(dialog, DialogInterface.BUTTON_POSITIVE);
- dialog.dismiss();
- ToastUtil.showShort(context, R.string.login_success);
- } else {
- loginBtn.setProgress(-1);
- }
- }, throwable -> {
- loginBtn.setProgress(-1);
- loginBtn.setText(R.string.unknown_error);
- });
-
- }
-
-}
diff --git a/app/src/main/java/eu/kanade/mangafeed/ui/setting/SettingsAccountsFragment.java b/app/src/main/java/eu/kanade/mangafeed/ui/setting/SettingsAccountsFragment.java
index 95cec0106..97485b8af 100644
--- a/app/src/main/java/eu/kanade/mangafeed/ui/setting/SettingsAccountsFragment.java
+++ b/app/src/main/java/eu/kanade/mangafeed/ui/setting/SettingsAccountsFragment.java
@@ -1,6 +1,7 @@
package eu.kanade.mangafeed.ui.setting;
import android.os.Bundle;
+import android.preference.PreferenceCategory;
import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
@@ -10,16 +11,21 @@ import javax.inject.Inject;
import eu.kanade.mangafeed.App;
import eu.kanade.mangafeed.R;
+import eu.kanade.mangafeed.data.chaptersync.BaseChapterSync;
+import eu.kanade.mangafeed.data.chaptersync.ChapterSyncManager;
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.activity.BaseActivity;
+import eu.kanade.mangafeed.ui.setting.dialog.ChapterSyncLoginDialog;
+import eu.kanade.mangafeed.ui.setting.dialog.SourceLoginDialog;
import rx.Observable;
public class SettingsAccountsFragment extends PreferenceFragment {
- @Inject SourceManager sourceManager;
@Inject PreferencesHelper preferences;
+ @Inject SourceManager sourceManager;
+ @Inject ChapterSyncManager syncManager;
public static SettingsAccountsFragment newInstance() {
return new SettingsAccountsFragment();
@@ -35,13 +41,30 @@ public class SettingsAccountsFragment extends PreferenceFragment {
List