Browse Source

Improve MAL support (UI is very simple yet).

inorichi 10 năm trước cách đây
mục cha
commit
8dca7fe79a
30 tập tin đã thay đổi với 569 bổ sung392 xóa
  1. 1 1
      app/src/main/AndroidManifest.xml
  2. 0 21
      app/src/main/java/eu/kanade/mangafeed/data/chaptersync/BaseChapterSync.java
  3. 0 163
      app/src/main/java/eu/kanade/mangafeed/data/chaptersync/MyAnimeList.java
  4. 21 21
      app/src/main/java/eu/kanade/mangafeed/data/database/DatabaseHelper.java
  5. 5 5
      app/src/main/java/eu/kanade/mangafeed/data/database/DbOpenHelper.java
  6. 0 35
      app/src/main/java/eu/kanade/mangafeed/data/database/models/ChapterSync.java
  7. 41 0
      app/src/main/java/eu/kanade/mangafeed/data/database/models/MangaSync.java
  8. 8 2
      app/src/main/java/eu/kanade/mangafeed/data/database/tables/MangaSyncTable.java
  9. 9 6
      app/src/main/java/eu/kanade/mangafeed/data/mangasync/MangaSyncManager.java
  10. 28 0
      app/src/main/java/eu/kanade/mangafeed/data/mangasync/base/BaseMangaSync.java
  11. 256 0
      app/src/main/java/eu/kanade/mangafeed/data/mangasync/services/MyAnimeList.java
  12. 12 13
      app/src/main/java/eu/kanade/mangafeed/data/preference/PreferencesHelper.java
  13. 14 14
      app/src/main/java/eu/kanade/mangafeed/data/sync/UpdateMangaSyncService.java
  14. 0 17
      app/src/main/java/eu/kanade/mangafeed/event/UpdateChapterSyncEvent.java
  15. 17 0
      app/src/main/java/eu/kanade/mangafeed/event/UpdateMangaSyncEvent.java
  16. 3 3
      app/src/main/java/eu/kanade/mangafeed/injection/component/AppComponent.java
  17. 3 3
      app/src/main/java/eu/kanade/mangafeed/injection/module/DataModule.java
  18. 1 1
      app/src/main/java/eu/kanade/mangafeed/ui/library/LibraryPresenter.java
  19. 3 3
      app/src/main/java/eu/kanade/mangafeed/ui/manga/MangaActivity.java
  20. 21 21
      app/src/main/java/eu/kanade/mangafeed/ui/manga/myanimelist/MyAnimeListDialogFragment.java
  21. 13 6
      app/src/main/java/eu/kanade/mangafeed/ui/manga/myanimelist/MyAnimeListFragment.java
  22. 37 16
      app/src/main/java/eu/kanade/mangafeed/ui/manga/myanimelist/MyAnimeListPresenter.java
  23. 14 14
      app/src/main/java/eu/kanade/mangafeed/ui/reader/ReaderPresenter.java
  24. 10 10
      app/src/main/java/eu/kanade/mangafeed/ui/setting/SettingsAccountsFragment.java
  25. 8 8
      app/src/main/java/eu/kanade/mangafeed/ui/setting/preference/MangaSyncLoginDialog.java
  26. 36 1
      app/src/main/res/layout/fragment_myanimelist.xml
  27. 1 1
      app/src/main/res/values/arrays.xml
  28. 1 1
      app/src/main/res/values/keys.xml
  29. 1 1
      app/src/main/res/values/strings.xml
  30. 5 5
      app/src/main/res/xml/pref_downloads.xml

+ 1 - 1
app/src/main/AndroidManifest.xml

@@ -52,7 +52,7 @@
         <service android:name=".data.download.DownloadService"
             android:exported="false"/>
 
-        <service android:name=".data.chaptersync.UpdateChapterSyncService"
+        <service android:name=".data.sync.UpdateMangaSyncService"
             android:exported="false"/>
 
         <receiver

+ 0 - 21
app/src/main/java/eu/kanade/mangafeed/data/chaptersync/BaseChapterSync.java

@@ -1,21 +0,0 @@
-package eu.kanade.mangafeed.data.chaptersync;
-
-import com.squareup.okhttp.Response;
-
-import eu.kanade.mangafeed.data.database.models.ChapterSync;
-import rx.Observable;
-
-public abstract class BaseChapterSync {
-
-    // Name of the chapter sync service to display
-    public abstract String getName();
-
-    // Id of the sync service (must be declared and obtained from ChapterSyncManager to avoid conflicts)
-    public abstract int getId();
-
-    public abstract Observable<Boolean> login(String username, String password);
-
-    public abstract boolean isLogged();
-
-    public abstract Observable<Response> update(ChapterSync chapter);
-}

+ 0 - 163
app/src/main/java/eu/kanade/mangafeed/data/chaptersync/MyAnimeList.java

@@ -1,163 +0,0 @@
-package eu.kanade.mangafeed.data.chaptersync;
-
-import android.content.Context;
-import android.net.Uri;
-import android.util.Xml;
-
-import com.squareup.okhttp.Credentials;
-import com.squareup.okhttp.FormEncodingBuilder;
-import com.squareup.okhttp.Headers;
-import com.squareup.okhttp.Response;
-
-import org.jsoup.Jsoup;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.IOException;
-import java.io.StringWriter;
-import java.util.List;
-
-import javax.inject.Inject;
-
-import eu.kanade.mangafeed.App;
-import eu.kanade.mangafeed.data.database.models.ChapterSync;
-import eu.kanade.mangafeed.data.network.NetworkHelper;
-import eu.kanade.mangafeed.data.preference.PreferencesHelper;
-import rx.Observable;
-
-public class MyAnimeList extends BaseChapterSync {
-
-    @Inject PreferencesHelper preferences;
-    @Inject NetworkHelper networkService;
-
-    private Headers headers;
-
-    public static final String BASE_URL = "http://myanimelist.net";
-
-    private static final String ENTRY = "entry";
-    private static final String CHAPTER = "chapter";
-
-    public MyAnimeList(Context context) {
-        App.get(context).getComponent().inject(this);
-
-        String username = preferences.getChapterSyncUsername(this);
-        String password = preferences.getChapterSyncPassword(this);
-
-        if (!username.isEmpty() && !password.isEmpty()) {
-            createHeaders(username, password);
-        }
-    }
-
-    @Override
-    public String getName() {
-        return "MyAnimeList";
-    }
-
-    @Override
-    public int getId() {
-        return ChapterSyncManager.MYANIMELIST;
-    }
-
-    public String getLoginUrl() {
-        return Uri.parse(BASE_URL).buildUpon()
-                .appendEncodedPath("api/account/verify_credentials.xml")
-                .toString();
-    }
-
-    public Observable<Boolean> login(String username, String password) {
-        createHeaders(username, password);
-        return networkService.getResponse(getLoginUrl(), headers, null)
-                .map(response -> response.code() == 200);
-    }
-
-    @Override
-    public boolean isLogged() {
-        return !preferences.getChapterSyncUsername(this).isEmpty()
-                && !preferences.getChapterSyncPassword(this).isEmpty();
-    }
-
-    public String getSearchUrl(String query) {
-        return Uri.parse(BASE_URL).buildUpon()
-                .appendEncodedPath("api/manga/search.xml")
-                .appendQueryParameter("q", query)
-                .toString();
-    }
-
-    public Observable<List<ChapterSync>> search(String query) {
-        return networkService.getStringResponse(getSearchUrl(query), headers, null)
-                .map(Jsoup::parse)
-                .flatMap(doc -> Observable.from(doc.select("entry")))
-                .map(entry -> {
-                    ChapterSync chapter = ChapterSync.create(this);
-                    chapter.title = entry.select("title").first().text();
-                    chapter.remote_id = Integer.parseInt(entry.select("id").first().text());
-                    return chapter;
-                })
-                .toList();
-    }
-
-    public String getListUrl(String username) {
-        return Uri.parse(BASE_URL).buildUpon()
-                .appendPath("malappinfo.php")
-                .appendQueryParameter("u", username)
-                .appendQueryParameter("status", "all")
-                .appendQueryParameter("type", "manga")
-                .toString();
-    }
-
-    public Observable<List<ChapterSync>> getList(String username) {
-        return networkService.getStringResponse(getListUrl(username), headers, null)
-                .map(Jsoup::parse)
-                .flatMap(doc -> Observable.from(doc.select("manga")))
-                .map(entry -> {
-                    ChapterSync chapter = ChapterSync.create(this);
-                    chapter.title = entry.select("series_title").first().text();
-                    chapter.remote_id = Integer.parseInt(
-                            entry.select("series_mangadb_id").first().text());
-                    chapter.last_chapter_read = Integer.parseInt(
-                            entry.select("my_read_chapters").first().text());
-                    return chapter;
-                })
-                .toList();
-    }
-
-    public String getUpdateUrl(ChapterSync chapter) {
-        return Uri.parse(BASE_URL).buildUpon()
-                .appendEncodedPath("api/mangalist/update")
-                .appendPath(chapter.remote_id + ".xml")
-                .toString();
-    }
-
-    public Observable<Response> update(ChapterSync chapter) {
-        XmlSerializer xml = Xml.newSerializer();
-        StringWriter writer = new StringWriter();
-        try {
-            xml.setOutput(writer);
-            xml.startDocument("UTF-8", false);
-            xml.startTag("", ENTRY);
-            xml.startTag("", CHAPTER);
-            xml.text(chapter.last_chapter_read + "");
-            xml.endTag("", CHAPTER);
-            xml.endTag("", ENTRY);
-            xml.endDocument();
-        } catch (IOException e) {
-            return Observable.error(e);
-        }
-
-        FormEncodingBuilder form = new FormEncodingBuilder();
-        form.add("data", writer.toString());
-
-        return networkService.postData(getUpdateUrl(chapter), form.build(), headers);
-    }
-
-    public void createHeaders(String username, String password) {
-        Headers.Builder builder = new Headers.Builder();
-        builder.add("Authorization", Credentials.basic(username, password));
-//        builder.add("User-Agent", "");
-        setHeaders(builder.build());
-    }
-
-    public void setHeaders(Headers headers) {
-        this.headers = headers;
-    }
-
-}

+ 21 - 21
app/src/main/java/eu/kanade/mangafeed/data/database/DatabaseHelper.java

@@ -16,21 +16,21 @@ import com.pushtorefresh.storio.sqlite.queries.RawQuery;
 
 import java.util.List;
 
-import eu.kanade.mangafeed.data.chaptersync.BaseChapterSync;
+import eu.kanade.mangafeed.data.database.models.MangaSync;
+import eu.kanade.mangafeed.data.mangasync.base.BaseMangaSync;
 import eu.kanade.mangafeed.data.database.models.Chapter;
 import eu.kanade.mangafeed.data.database.models.ChapterStorIOSQLiteDeleteResolver;
 import eu.kanade.mangafeed.data.database.models.ChapterStorIOSQLiteGetResolver;
 import eu.kanade.mangafeed.data.database.models.ChapterStorIOSQLitePutResolver;
-import eu.kanade.mangafeed.data.database.models.ChapterSync;
-import eu.kanade.mangafeed.data.database.models.ChapterSyncStorIOSQLiteDeleteResolver;
-import eu.kanade.mangafeed.data.database.models.ChapterSyncStorIOSQLiteGetResolver;
-import eu.kanade.mangafeed.data.database.models.ChapterSyncStorIOSQLitePutResolver;
 import eu.kanade.mangafeed.data.database.models.Manga;
 import eu.kanade.mangafeed.data.database.models.MangaStorIOSQLiteDeleteResolver;
 import eu.kanade.mangafeed.data.database.models.MangaStorIOSQLiteGetResolver;
 import eu.kanade.mangafeed.data.database.models.MangaStorIOSQLitePutResolver;
+import eu.kanade.mangafeed.data.database.models.MangaSyncStorIOSQLiteDeleteResolver;
+import eu.kanade.mangafeed.data.database.models.MangaSyncStorIOSQLiteGetResolver;
+import eu.kanade.mangafeed.data.database.models.MangaSyncStorIOSQLitePutResolver;
 import eu.kanade.mangafeed.data.database.resolvers.MangaWithUnreadGetResolver;
-import eu.kanade.mangafeed.data.database.tables.ChapterSyncTable;
+import eu.kanade.mangafeed.data.database.tables.MangaSyncTable;
 import eu.kanade.mangafeed.data.database.tables.ChapterTable;
 import eu.kanade.mangafeed.data.database.tables.MangaTable;
 import eu.kanade.mangafeed.util.ChapterRecognition;
@@ -55,10 +55,10 @@ public class DatabaseHelper {
                         .getResolver(new ChapterStorIOSQLiteGetResolver())
                         .deleteResolver(new ChapterStorIOSQLiteDeleteResolver())
                         .build())
-                .addTypeMapping(ChapterSync.class, SQLiteTypeMapping.<ChapterSync>builder()
-                        .putResolver(new ChapterSyncStorIOSQLitePutResolver())
-                        .getResolver(new ChapterSyncStorIOSQLiteGetResolver())
-                        .deleteResolver(new ChapterSyncStorIOSQLiteDeleteResolver())
+                .addTypeMapping(MangaSync.class, SQLiteTypeMapping.<MangaSync>builder()
+                        .putResolver(new MangaSyncStorIOSQLitePutResolver())
+                        .getResolver(new MangaSyncStorIOSQLiteGetResolver())
+                        .deleteResolver(new MangaSyncStorIOSQLiteDeleteResolver())
                         .build())
                 .build();
     }
@@ -88,7 +88,7 @@ public class DatabaseHelper {
                 .prepare();
     }
 
-    public PreparedGetListOfObjects<Manga> getMangasWithUnread() {
+    public PreparedGetListOfObjects<Manga> getFavoriteMangasWithUnread() {
         return db.get()
                 .listOfObjects(Manga.class)
                 .withQuery(RawQuery.builder()
@@ -301,30 +301,30 @@ public class DatabaseHelper {
                 .prepare();
     }
 
-    // Chapter sync related queries
+    // Manga sync related queries
 
-    public PreparedGetListOfObjects<ChapterSync> getChapterSync(Manga manga, BaseChapterSync sync) {
+    public PreparedGetListOfObjects<MangaSync> getMangaSync(Manga manga, BaseMangaSync sync) {
 
         return db.get()
-                .listOfObjects(ChapterSync.class)
+                .listOfObjects(MangaSync.class)
                 .withQuery(Query.builder()
-                        .table(ChapterSyncTable.TABLE)
-                        .where(ChapterSyncTable.COLUMN_MANGA_ID + "=? AND " +
-                                ChapterSyncTable.COLUMN_SYNC_ID + "=?")
+                        .table(MangaSyncTable.TABLE)
+                        .where(MangaSyncTable.COLUMN_MANGA_ID + "=? AND " +
+                                MangaSyncTable.COLUMN_SYNC_ID + "=?")
                         .whereArgs(manga.id, sync.getId())
                         .build())
                 .prepare();
     }
 
-    public PreparedPutObject<ChapterSync> insertChapterSync(ChapterSync chapter) {
+    public PreparedPutObject<MangaSync> insertMangaSync(MangaSync manga) {
         return db.put()
-                .object(chapter)
+                .object(manga)
                 .prepare();
     }
 
-    public PreparedDeleteObject<ChapterSync> deleteChapterSync(ChapterSync chapter) {
+    public PreparedDeleteObject<MangaSync> deleteMangaSync(MangaSync manga) {
         return db.delete()
-                .object(chapter)
+                .object(manga)
                 .prepare();
     }
 }

+ 5 - 5
app/src/main/java/eu/kanade/mangafeed/data/database/DbOpenHelper.java

@@ -5,14 +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.MangaSyncTable;
 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 = 2;
+    public static final int DATABASE_VERSION = 3;
 
     public DbOpenHelper(@NonNull Context context) {
         super(context, DATABASE_NAME, null, DATABASE_VERSION);
@@ -22,13 +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());
+        db.execSQL(MangaSyncTable.getCreateTableQuery());
     }
 
     @Override
     public void onUpgrade(@NonNull SQLiteDatabase db, int oldVersion, int newVersion) {
-        if (oldVersion == 1)
-            db.execSQL(ChapterSyncTable.getCreateTableQuery());
+        if (oldVersion < 3)
+            db.execSQL(MangaSyncTable.getCreateTableQuery());
     }
 
     @Override

+ 0 - 35
app/src/main/java/eu/kanade/mangafeed/data/database/models/ChapterSync.java

@@ -1,35 +0,0 @@
-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;
-    }
-}

+ 41 - 0
app/src/main/java/eu/kanade/mangafeed/data/database/models/MangaSync.java

@@ -0,0 +1,41 @@
+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.mangasync.base.BaseMangaSync;
+import eu.kanade.mangafeed.data.database.tables.MangaSyncTable;
+
+@StorIOSQLiteType(table = MangaSyncTable.TABLE)
+public class MangaSync {
+
+    @StorIOSQLiteColumn(name = MangaSyncTable.COLUMN_ID, key = true)
+    public Long id;
+
+    @StorIOSQLiteColumn(name = MangaSyncTable.COLUMN_MANGA_ID)
+    public long manga_id;
+
+    @StorIOSQLiteColumn(name = MangaSyncTable.COLUMN_SYNC_ID)
+    public int sync_id;
+
+    @StorIOSQLiteColumn(name = MangaSyncTable.COLUMN_REMOTE_ID)
+    public int remote_id;
+
+    @StorIOSQLiteColumn(name = MangaSyncTable.COLUMN_TITLE)
+    public String title;
+
+    @StorIOSQLiteColumn(name = MangaSyncTable.COLUMN_LAST_CHAPTER_READ)
+    public int last_chapter_read;
+
+    @StorIOSQLiteColumn(name = MangaSyncTable.COLUMN_SCORE)
+    public float score;
+
+    @StorIOSQLiteColumn(name = MangaSyncTable.COLUMN_STATUS)
+    public int status;
+
+    public static MangaSync create(BaseMangaSync service) {
+        MangaSync mangasync = new MangaSync();
+        mangasync.sync_id = service.getId();
+        return mangasync;
+    }
+}

+ 8 - 2
app/src/main/java/eu/kanade/mangafeed/data/database/tables/ChapterSyncTable.java → app/src/main/java/eu/kanade/mangafeed/data/database/tables/MangaSyncTable.java

@@ -2,9 +2,9 @@ package eu.kanade.mangafeed.data.database.tables;
 
 import android.support.annotation.NonNull;
 
-public class ChapterSyncTable {
+public class MangaSyncTable {
 
-    public static final String TABLE = "chapter_sync";
+    public static final String TABLE = "manga_sync";
 
     public static final String COLUMN_ID = "_id";
 
@@ -18,6 +18,10 @@ public class ChapterSyncTable {
 
     public static final String COLUMN_LAST_CHAPTER_READ = "last_chapter_read";
 
+    public static final String COLUMN_STATUS = "status";
+
+    public static final String COLUMN_SCORE = "score";
+
     @NonNull
     public static String getCreateTableQuery() {
         return "CREATE TABLE " + TABLE + "("
@@ -27,6 +31,8 @@ public class ChapterSyncTable {
                 + COLUMN_REMOTE_ID + " INTEGER NOT NULL, "
                 + COLUMN_TITLE + " TEXT NOT NULL, "
                 + COLUMN_LAST_CHAPTER_READ + " INTEGER NOT NULL, "
+                + COLUMN_STATUS + " INTEGER NOT NULL, "
+                + COLUMN_SCORE + " FLOAT NOT NULL, "
                 + "FOREIGN KEY(" + COLUMN_MANGA_ID + ") REFERENCES " + MangaTable.TABLE + "(" + MangaTable.COLUMN_ID + ") "
                 + "ON DELETE CASCADE"
                 + ");";

+ 9 - 6
app/src/main/java/eu/kanade/mangafeed/data/chaptersync/ChapterSyncManager.java → app/src/main/java/eu/kanade/mangafeed/data/mangasync/MangaSyncManager.java

@@ -1,18 +1,21 @@
-package eu.kanade.mangafeed.data.chaptersync;
+package eu.kanade.mangafeed.data.mangasync;
 
 import android.content.Context;
 
 import java.util.ArrayList;
 import java.util.List;
 
-public class ChapterSyncManager {
+import eu.kanade.mangafeed.data.mangasync.base.BaseMangaSync;
+import eu.kanade.mangafeed.data.mangasync.services.MyAnimeList;
 
-    private List<BaseChapterSync> services;
+public class MangaSyncManager {
+
+    private List<BaseMangaSync> services;
     private MyAnimeList myAnimeList;
 
     public static final int MYANIMELIST = 1;
 
-    public ChapterSyncManager(Context context) {
+    public MangaSyncManager(Context context) {
         services = new ArrayList<>();
         myAnimeList = new MyAnimeList(context);
         services.add(myAnimeList);
@@ -22,11 +25,11 @@ public class ChapterSyncManager {
         return myAnimeList;
     }
 
-    public List<BaseChapterSync> getChapterSyncServices() {
+    public List<BaseMangaSync> getSyncServices() {
         return services;
     }
 
-    public BaseChapterSync getSyncService(int id) {
+    public BaseMangaSync getSyncService(int id) {
         switch (id) {
             case MYANIMELIST:
                 return myAnimeList;

+ 28 - 0
app/src/main/java/eu/kanade/mangafeed/data/mangasync/base/BaseMangaSync.java

@@ -0,0 +1,28 @@
+package eu.kanade.mangafeed.data.mangasync.base;
+
+import com.squareup.okhttp.Response;
+
+import eu.kanade.mangafeed.data.database.models.MangaSync;
+import rx.Observable;
+
+public abstract class BaseMangaSync {
+
+    // Name of the manga sync service to display
+    public abstract String getName();
+
+    // Id of the sync service (must be declared and obtained from MangaSyncManager to avoid conflicts)
+    public abstract int getId();
+
+    public abstract Observable<Boolean> login(String username, String password);
+
+    public abstract boolean isLogged();
+
+    public abstract Observable<Response> update(MangaSync manga);
+
+    public abstract Observable<Response> add(MangaSync manga);
+
+    public abstract Observable<Response> bind(MangaSync manga);
+
+    public abstract String getStatus(int status);
+
+}

+ 256 - 0
app/src/main/java/eu/kanade/mangafeed/data/mangasync/services/MyAnimeList.java

@@ -0,0 +1,256 @@
+package eu.kanade.mangafeed.data.mangasync.services;
+
+import android.content.Context;
+import android.net.Uri;
+import android.util.Xml;
+
+import com.squareup.okhttp.Credentials;
+import com.squareup.okhttp.FormEncodingBuilder;
+import com.squareup.okhttp.Headers;
+import com.squareup.okhttp.RequestBody;
+import com.squareup.okhttp.Response;
+
+import org.jsoup.Jsoup;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import eu.kanade.mangafeed.App;
+import eu.kanade.mangafeed.data.database.models.MangaSync;
+import eu.kanade.mangafeed.data.mangasync.MangaSyncManager;
+import eu.kanade.mangafeed.data.mangasync.base.BaseMangaSync;
+import eu.kanade.mangafeed.data.network.NetworkHelper;
+import eu.kanade.mangafeed.data.preference.PreferencesHelper;
+import rx.Observable;
+
+public class MyAnimeList extends BaseMangaSync {
+
+    @Inject PreferencesHelper preferences;
+    @Inject NetworkHelper networkService;
+
+    private Headers headers;
+    private String username;
+
+    public static final String BASE_URL = "http://myanimelist.net";
+
+    private static final String ENTRY_TAG = "entry";
+    private static final String CHAPTER_TAG = "chapter";
+    private static final String SCORE_TAG = "score";
+    private static final String STATUS_TAG = "status";
+
+    public static final int NOT_IN_LIST = 0;
+    public static final int READING = 1;
+    public static final int COMPLETED = 2;
+    public static final int ON_HOLD = 3;
+    public static final int DROPPED = 4;
+    public static final int PLAN_TO_READ = 6;
+
+    public static final int DEFAULT_STATUS = READING;
+    public static final int DEFAULT_SCORE = 0;
+
+    public MyAnimeList(Context context) {
+        App.get(context).getComponent().inject(this);
+
+        String username = preferences.getMangaSyncUsername(this);
+        String password = preferences.getMangaSyncPassword(this);
+
+        if (!username.isEmpty() && !password.isEmpty()) {
+            createHeaders(username, password);
+        }
+    }
+
+    @Override
+    public String getName() {
+        return "MyAnimeList";
+    }
+
+    @Override
+    public int getId() {
+        return MangaSyncManager.MYANIMELIST;
+    }
+
+    public String getLoginUrl() {
+        return Uri.parse(BASE_URL).buildUpon()
+                .appendEncodedPath("api/account/verify_credentials.xml")
+                .toString();
+    }
+
+    public Observable<Boolean> login(String username, String password) {
+        createHeaders(username, password);
+        return networkService.getResponse(getLoginUrl(), headers, null)
+                .map(response -> response.code() == 200);
+    }
+
+    @Override
+    public boolean isLogged() {
+        return !preferences.getMangaSyncUsername(this).isEmpty()
+                && !preferences.getMangaSyncPassword(this).isEmpty();
+    }
+
+    public String getSearchUrl(String query) {
+        return Uri.parse(BASE_URL).buildUpon()
+                .appendEncodedPath("api/manga/search.xml")
+                .appendQueryParameter("q", query)
+                .toString();
+    }
+
+    public Observable<List<MangaSync>> search(String query) {
+        return networkService.getStringResponse(getSearchUrl(query), headers, null)
+                .map(Jsoup::parse)
+                .flatMap(doc -> Observable.from(doc.select("entry")))
+                .map(entry -> {
+                    MangaSync manga = MangaSync.create(this);
+                    manga.title = entry.select("title").first().text();
+                    manga.remote_id = Integer.parseInt(entry.select("id").first().text());
+                    return manga;
+                })
+                .toList();
+    }
+
+    public String getListUrl(String username) {
+        return Uri.parse(BASE_URL).buildUpon()
+                .appendPath("malappinfo.php")
+                .appendQueryParameter("u", username)
+                .appendQueryParameter("status", "all")
+                .appendQueryParameter("type", "manga")
+                .toString();
+    }
+
+    public Observable<List<MangaSync>> getList(String username) {
+        // TODO cache this list for a few minutes
+        return networkService.getStringResponse(getListUrl(username), headers, null)
+                .map(Jsoup::parse)
+                .flatMap(doc -> Observable.from(doc.select("manga")))
+                .map(entry -> {
+                    MangaSync manga = MangaSync.create(this);
+                    manga.title = entry.select("series_title").first().text();
+                    manga.remote_id = Integer.parseInt(
+                            entry.select("series_mangadb_id").first().text());
+                    manga.last_chapter_read = Integer.parseInt(
+                            entry.select("my_read_chapters").first().text());
+                    manga.status = Integer.parseInt(
+                            entry.select("my_status").first().text());
+                    // MAL doesn't support score with decimals
+                    manga.score = Integer.parseInt(
+                            entry.select("my_score").first().text());
+                    return manga;
+                })
+                .toList();
+    }
+
+    public String getUpdateUrl(MangaSync manga) {
+        return Uri.parse(BASE_URL).buildUpon()
+                .appendEncodedPath("api/mangalist/update")
+                .appendPath(manga.remote_id + ".xml")
+                .toString();
+    }
+
+    public Observable<Response> update(MangaSync manga) {
+        try {
+            RequestBody payload = getMangaPostPayload(manga);
+            return networkService.postData(getUpdateUrl(manga), payload, headers);
+        } catch (IOException e) {
+            return Observable.error(e);
+        }
+    }
+
+    public String getAddUrl(MangaSync manga) {
+        return Uri.parse(BASE_URL).buildUpon()
+                .appendEncodedPath("api/mangalist/add")
+                .appendPath(manga.remote_id + ".xml")
+                .toString();
+    }
+
+    public Observable<Response> add(MangaSync manga) {
+        try {
+            RequestBody payload = getMangaPostPayload(manga);
+            return networkService.postData(getAddUrl(manga), payload, headers);
+        } catch (IOException e) {
+            return Observable.error(e);
+        }
+    }
+
+    private RequestBody getMangaPostPayload(MangaSync manga) throws IOException {
+        XmlSerializer xml = Xml.newSerializer();
+        StringWriter writer = new StringWriter();
+        xml.setOutput(writer);
+        xml.startDocument("UTF-8", false);
+        xml.startTag("", ENTRY_TAG);
+
+        // Last chapter read
+        if (manga.last_chapter_read != 0) {
+            xml.startTag("", CHAPTER_TAG);
+            xml.text(manga.last_chapter_read + "");
+            xml.endTag("", CHAPTER_TAG);
+        }
+        // Manga status in the list
+        xml.startTag("", STATUS_TAG);
+        xml.text(manga.status + "");
+        xml.endTag("", STATUS_TAG);
+        // Manga score
+        xml.startTag("", SCORE_TAG);
+        xml.text(manga.score + "");
+        xml.endTag("", SCORE_TAG);
+
+        xml.endTag("", ENTRY_TAG);
+        xml.endDocument();
+
+        FormEncodingBuilder form = new FormEncodingBuilder();
+        form.add("data", writer.toString());
+        return form.build();
+    }
+
+    public Observable<Response> bind(MangaSync manga) {
+        return getList(username)
+                .flatMap(list -> {
+                    manga.sync_id = getId();
+                    for (MangaSync remoteManga : list) {
+                        if (remoteManga.remote_id == manga.remote_id) {
+                            // Manga is already in the list
+                            manga.score = remoteManga.score;
+                            manga.status = remoteManga.status;
+                            manga.last_chapter_read = remoteManga.last_chapter_read;
+                            return update(manga);
+                        }
+                    }
+                    // Set default fields if it's not found in the list
+                    manga.score = DEFAULT_SCORE;
+                    manga.status = DEFAULT_STATUS;
+                    return add(manga);
+                });
+    }
+
+    @Override
+    public String getStatus(int status) {
+        switch (status) {
+            case READING:
+                return "Reading";
+            case COMPLETED:
+                return "Completed";
+            case ON_HOLD:
+                return "On hold";
+            case DROPPED:
+                return "Dropped";
+            case PLAN_TO_READ:
+                return "Plan to read";
+        }
+        return "";
+    }
+
+    public void createHeaders(String username, String password) {
+        this.username = username;
+        Headers.Builder builder = new Headers.Builder();
+        builder.add("Authorization", Credentials.basic(username, password));
+        builder.add("User-Agent", "api-indiv-9F93C52A963974CF674325391990191C");
+        setHeaders(builder.build());
+    }
+
+    public void setHeaders(Headers headers) {
+        this.headers = headers;
+    }
+
+}

+ 12 - 13
app/src/main/java/eu/kanade/mangafeed/data/preference/PreferencesHelper.java

@@ -11,7 +11,7 @@ import com.f2prateek.rx.preferences.RxSharedPreferences;
 import java.io.File;
 
 import eu.kanade.mangafeed.R;
-import eu.kanade.mangafeed.data.chaptersync.BaseChapterSync;
+import eu.kanade.mangafeed.data.mangasync.base.BaseMangaSync;
 import eu.kanade.mangafeed.data.source.base.Source;
 import rx.Observable;
 
@@ -23,8 +23,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_";
+    private static final String MANGASYNC_ACCOUNT_USERNAME = "pref_mangasync_username_";
+    private static final String MANGASYNC_ACCOUNT_PASSWORD = "pref_mangasync_password_";
 
     private File defaultDownloadsDir;
 
@@ -102,18 +102,18 @@ public class PreferencesHelper {
                 .apply();
     }
 
-    public String getChapterSyncUsername(BaseChapterSync sync) {
-        return prefs.getString(CHAPTERSYNC_ACCOUNT_USERNAME + sync.getId(), "");
+    public String getMangaSyncUsername(BaseMangaSync sync) {
+        return prefs.getString(MANGASYNC_ACCOUNT_USERNAME + sync.getId(), "");
     }
 
-    public String getChapterSyncPassword(BaseChapterSync sync) {
-        return prefs.getString(CHAPTERSYNC_ACCOUNT_PASSWORD + sync.getId(), "");
+    public String getMangaSyncPassword(BaseMangaSync sync) {
+        return prefs.getString(MANGASYNC_ACCOUNT_PASSWORD + sync.getId(), "");
     }
 
-    public void setChapterSyncCredentials(BaseChapterSync sync, String username, String password) {
+    public void setMangaSyncCredentials(BaseMangaSync sync, String username, String password) {
         prefs.edit()
-                .putString(CHAPTERSYNC_ACCOUNT_USERNAME + sync.getId(), username)
-                .putString(CHAPTERSYNC_ACCOUNT_PASSWORD + sync.getId(), password)
+                .putString(MANGASYNC_ACCOUNT_USERNAME + sync.getId(), username)
+                .putString(MANGASYNC_ACCOUNT_PASSWORD + sync.getId(), password)
                 .apply();
     }
 
@@ -127,12 +127,11 @@ public class PreferencesHelper {
     }
 
     public int getDownloadThreads() {
-        return Integer.parseInt(prefs.getString(getKey(R.string.pref_download_threads_key), "1"));
+        return prefs.getInt(getKey(R.string.pref_download_slots_key), 1);
     }
 
     public Observable<Integer> getDownloadTheadsObservable() {
-        return rxPrefs.getString(getKey(R.string.pref_download_threads_key), "1")
-                .asObservable().map(Integer::parseInt);
+        return rxPrefs.getInteger(getKey(R.string.pref_download_slots_key), 1).asObservable();
     }
 
 }

+ 14 - 14
app/src/main/java/eu/kanade/mangafeed/data/chaptersync/UpdateChapterSyncService.java → app/src/main/java/eu/kanade/mangafeed/data/sync/UpdateMangaSyncService.java

@@ -1,4 +1,4 @@
-package eu.kanade.mangafeed.data.chaptersync;
+package eu.kanade.mangafeed.data.sync;
 
 import android.app.Service;
 import android.content.Context;
@@ -10,24 +10,24 @@ import javax.inject.Inject;
 import de.greenrobot.event.EventBus;
 import eu.kanade.mangafeed.App;
 import eu.kanade.mangafeed.data.database.DatabaseHelper;
-import eu.kanade.mangafeed.data.database.models.ChapterSync;
-import eu.kanade.mangafeed.data.network.NetworkHelper;
-import eu.kanade.mangafeed.event.UpdateChapterSyncEvent;
+import eu.kanade.mangafeed.data.database.models.MangaSync;
+import eu.kanade.mangafeed.data.mangasync.MangaSyncManager;
+import eu.kanade.mangafeed.data.mangasync.base.BaseMangaSync;
+import eu.kanade.mangafeed.event.UpdateMangaSyncEvent;
 import eu.kanade.mangafeed.util.EventBusHook;
 import rx.android.schedulers.AndroidSchedulers;
 import rx.schedulers.Schedulers;
 import rx.subscriptions.CompositeSubscription;
 
-public class UpdateChapterSyncService extends Service {
+public class UpdateMangaSyncService extends Service {
 
-    @Inject ChapterSyncManager syncManager;
-    @Inject NetworkHelper networkManager;
+    @Inject MangaSyncManager syncManager;
     @Inject DatabaseHelper db;
 
     private CompositeSubscription subscriptions;
 
     public static void start(Context context) {
-        context.startService(new Intent(context, UpdateChapterSyncService.class));
+        context.startService(new Intent(context, UpdateMangaSyncService.class));
     }
 
     @Override
@@ -56,15 +56,15 @@ public class UpdateChapterSyncService extends Service {
     }
 
     @EventBusHook
-    public void onEventMainThread(UpdateChapterSyncEvent event) {
-        updateLastChapteRead(event.getChapterSync());
+    public void onEventMainThread(UpdateMangaSyncEvent event) {
+        updateLastChapteRead(event.getMangaSync());
     }
 
-    private void updateLastChapteRead(ChapterSync chapterSync) {
-        BaseChapterSync sync = syncManager.getSyncService(chapterSync.sync_id);
+    private void updateLastChapteRead(MangaSync mangaSync) {
+        BaseMangaSync sync = syncManager.getSyncService(mangaSync.sync_id);
 
-        subscriptions.add(sync.update(chapterSync)
-                .flatMap(response -> db.insertChapterSync(chapterSync).createObservable())
+        subscriptions.add(sync.update(mangaSync)
+                .flatMap(response -> db.insertMangaSync(mangaSync).createObservable())
                 .subscribeOn(Schedulers.io())
                 .observeOn(AndroidSchedulers.mainThread())
                 .subscribe(result -> {

+ 0 - 17
app/src/main/java/eu/kanade/mangafeed/event/UpdateChapterSyncEvent.java

@@ -1,17 +0,0 @@
-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;
-    }
-
-}

+ 17 - 0
app/src/main/java/eu/kanade/mangafeed/event/UpdateMangaSyncEvent.java

@@ -0,0 +1,17 @@
+package eu.kanade.mangafeed.event;
+
+import eu.kanade.mangafeed.data.database.models.MangaSync;
+
+public class UpdateMangaSyncEvent {
+
+    private MangaSync mangaSync;
+
+    public UpdateMangaSyncEvent(MangaSync mangaSync) {
+        this.mangaSync = mangaSync;
+    }
+
+    public MangaSync getMangaSync() {
+        return mangaSync;
+    }
+
+}

+ 3 - 3
app/src/main/java/eu/kanade/mangafeed/injection/component/AppComponent.java

@@ -5,8 +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.mangasync.services.MyAnimeList;
+import eu.kanade.mangafeed.data.sync.UpdateMangaSyncService;
 import eu.kanade.mangafeed.data.download.DownloadService;
 import eu.kanade.mangafeed.data.source.base.Source;
 import eu.kanade.mangafeed.data.sync.LibraryUpdateService;
@@ -56,7 +56,7 @@ public interface AppComponent {
 
     void inject(LibraryUpdateService libraryUpdateService);
     void inject(DownloadService downloadService);
-    void inject(UpdateChapterSyncService updateChapterSyncService);
+    void inject(UpdateMangaSyncService updateMangaSyncService);
 
     Application application();
 

+ 3 - 3
app/src/main/java/eu/kanade/mangafeed/injection/module/DataModule.java

@@ -8,7 +8,7 @@ import dagger.Module;
 import dagger.Provides;
 import eu.kanade.mangafeed.data.cache.CacheManager;
 import eu.kanade.mangafeed.data.cache.CoverCache;
-import eu.kanade.mangafeed.data.chaptersync.ChapterSyncManager;
+import eu.kanade.mangafeed.data.mangasync.MangaSyncManager;
 import eu.kanade.mangafeed.data.database.DatabaseHelper;
 import eu.kanade.mangafeed.data.download.DownloadManager;
 import eu.kanade.mangafeed.data.network.NetworkHelper;
@@ -66,8 +66,8 @@ public class DataModule {
 
     @Provides
     @Singleton
-    ChapterSyncManager provideChapterSyncManager(Application app) {
-        return new ChapterSyncManager(app);
+    MangaSyncManager provideMangaSyncManager(Application app) {
+        return new MangaSyncManager(app);
     }
 
 }

+ 1 - 1
app/src/main/java/eu/kanade/mangafeed/ui/library/LibraryPresenter.java

@@ -28,7 +28,7 @@ public class LibraryPresenter extends BasePresenter<LibraryFragment> {
         super.onCreate(savedState);
 
         restartableLatestCache(GET_MANGAS,
-                () -> db.getMangasWithUnread().createObservable()
+                () -> db.getFavoriteMangasWithUnread().createObservable()
                         .subscribeOn(Schedulers.io())
                         .observeOn(AndroidSchedulers.mainThread()),
                 LibraryFragment::onNextMangas);

+ 3 - 3
app/src/main/java/eu/kanade/mangafeed/ui/manga/MangaActivity.java

@@ -17,7 +17,7 @@ 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.mangasync.MangaSyncManager;
 import eu.kanade.mangafeed.data.database.models.Manga;
 import eu.kanade.mangafeed.data.preference.PreferencesHelper;
 import eu.kanade.mangafeed.ui.base.activity.BaseRxActivity;
@@ -34,7 +34,7 @@ public class MangaActivity extends BaseRxActivity<MangaPresenter> {
     @Bind(R.id.view_pager) ViewPager view_pager;
 
     @Inject PreferencesHelper preferences;
-    @Inject ChapterSyncManager chapterSyncManager;
+    @Inject MangaSyncManager mangaSyncManager;
 
     private MangaDetailAdapter adapter;
     private long manga_id;
@@ -116,7 +116,7 @@ public class MangaActivity extends BaseRxActivity<MangaPresenter> {
             };
 
             pageCount = 2;
-            if (chapterSyncManager.getMyAnimeList().isLogged())
+            if (!is_online && mangaSyncManager.getMyAnimeList().isLogged())
                 pageCount++;
         }
 

+ 21 - 21
app/src/main/java/eu/kanade/mangafeed/ui/manga/myanimelist/MyAnimeListDialogFragment.java

@@ -16,7 +16,7 @@ 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.data.database.models.MangaSync;
 import uk.co.ribot.easyadapter.EasyAdapter;
 import uk.co.ribot.easyadapter.ItemViewHolder;
 import uk.co.ribot.easyadapter.PositionInfo;
@@ -29,60 +29,60 @@ public class MyAnimeListDialogFragment extends DialogFragment {
     @Bind(R.id.myanimelist_search_button) Button searchButton;
     @Bind(R.id.myanimelist_search_results) ListView searchResults;
 
-    private EasyAdapter<ChapterSync> adapter;
+    private EasyAdapter<MangaSync> adapter;
     private MyAnimeListFragment fragment;
-    private ChapterSync selectedItem;
+    private MyAnimeListPresenter presenter;
+    private MangaSync selectedItem;
 
     public static MyAnimeListDialogFragment newInstance(MyAnimeListFragment parentFragment) {
         MyAnimeListDialogFragment dialog = new MyAnimeListDialogFragment();
-        dialog.setParentFragment(parentFragment);
+        dialog.fragment = parentFragment;
+        dialog.presenter = parentFragment.getPresenter();
         return dialog;
     }
 
     @Override
     public Dialog onCreateDialog(Bundle savedState) {
-        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
-
+        // Inflate and bind view
         LayoutInflater inflater = getActivity().getLayoutInflater();
-
         View view = inflater.inflate(R.layout.dialog_myanimelist_search, null);
         ButterKnife.bind(this, view);
 
-
+        // Build dialog
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
         builder.setView(view)
                 .setPositiveButton(R.string.button_ok, (dialog, which) -> onPositiveButtonClick())
                 .setNegativeButton(R.string.button_cancel, (dialog, which) -> {});
 
+        // Create adapter
+        adapter = new EasyAdapter<>(getActivity(), ResultViewHolder.class);
+        searchResults.setAdapter(adapter);
+
+        // Set listeners
         searchButton.setOnClickListener(v ->
-                fragment.getPresenter().searchManga(searchText.getText().toString()));
+                presenter.searchManga(searchText.getText().toString()));
 
         searchResults.setOnItemClickListener((parent, viewList, position, id) ->
                 selectedItem = adapter.getItem(position));
 
-        adapter = new EasyAdapter<>(getActivity(), ResultViewHolder.class);
-
-        searchResults.setAdapter(adapter);
-
+        // Do an initial search based on the manga's title
+        presenter.searchManga(presenter.manga.title);
         return builder.create();
     }
 
     private void onPositiveButtonClick() {
         if (adapter != null && selectedItem != null) {
-            fragment.getPresenter().registerManga(selectedItem);
+            presenter.registerManga(selectedItem);
         }
     }
 
-    public void setResults(List<ChapterSync> results) {
+    public void setResults(List<MangaSync> 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<ChapterSync> {
+    public static class ResultViewHolder extends ItemViewHolder<MangaSync> {
 
         @ViewId(R.id.myanimelist_result_title) TextView title;
 
@@ -91,7 +91,7 @@ public class MyAnimeListDialogFragment extends DialogFragment {
         }
 
         @Override
-        public void onSetValues(ChapterSync chapter, PositionInfo positionInfo) {
+        public void onSetValues(MangaSync chapter, PositionInfo positionInfo) {
             title.setText(chapter.title);
         }
     }

+ 13 - 6
app/src/main/java/eu/kanade/mangafeed/ui/manga/myanimelist/MyAnimeListFragment.java

@@ -11,12 +11,13 @@ import android.widget.Button;
 import android.widget.EditText;
 import android.widget.TextView;
 
+import java.text.DecimalFormat;
 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.data.database.models.MangaSync;
 import eu.kanade.mangafeed.ui.base.fragment.BaseRxFragment;
 import nucleus.factory.RequiresPresenter;
 
@@ -24,11 +25,15 @@ import nucleus.factory.RequiresPresenter;
 public class MyAnimeListFragment extends BaseRxFragment<MyAnimeListPresenter> {
 
     @Bind(R.id.myanimelist_title) TextView title;
-    @Bind(R.id.myanimelist_last_chapter_read) EditText lastChapterRead;
+    @Bind(R.id.last_chapter_read) EditText lastChapterRead;
+    @Bind(R.id.score) TextView score;
+    @Bind(R.id.status) TextView status;
     @Bind(R.id.update_button) Button updateButton;
 
     private MyAnimeListDialogFragment dialog;
 
+    private DecimalFormat decimalFormat = new DecimalFormat("#.##");
+
     public static MyAnimeListFragment newInstance() {
         return new MyAnimeListFragment();
     }
@@ -66,9 +71,11 @@ public class MyAnimeListFragment extends BaseRxFragment<MyAnimeListPresenter> {
         return super.onOptionsItemSelected(item);
     }
 
-    public void setChapterSync(ChapterSync chapterSync) {
-        title.setText(chapterSync.title);
-        lastChapterRead.setText(chapterSync.last_chapter_read + "");
+    public void setMangaSync(MangaSync mangaSync) {
+        title.setText(mangaSync.title);
+        lastChapterRead.setText(mangaSync.last_chapter_read + "");
+        score.setText(decimalFormat.format(mangaSync.score));
+        status.setText(getPresenter().myAnimeList.getStatus(mangaSync.status));
     }
 
     private void showSearchDialog() {
@@ -78,7 +85,7 @@ public class MyAnimeListFragment extends BaseRxFragment<MyAnimeListPresenter> {
         dialog.show(getActivity().getSupportFragmentManager(), "search");
     }
 
-    public void onSearchResults(List<ChapterSync> results) {
+    public void onSearchResults(List<MangaSync> results) {
         if (dialog != null)
             dialog.setResults(results);
     }

+ 37 - 16
app/src/main/java/eu/kanade/mangafeed/ui/manga/myanimelist/MyAnimeListPresenter.java

@@ -4,13 +4,14 @@ 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.models.MangaSync;
+import eu.kanade.mangafeed.data.mangasync.MangaSyncManager;
+import eu.kanade.mangafeed.data.mangasync.services.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 eu.kanade.mangafeed.util.ToastUtil;
 import rx.Observable;
 import rx.Subscription;
 import rx.android.schedulers.AndroidSchedulers;
@@ -20,11 +21,11 @@ import timber.log.Timber;
 public class MyAnimeListPresenter extends BasePresenter<MyAnimeListFragment> {
 
     @Inject DatabaseHelper db;
-    @Inject ChapterSyncManager syncManager;
+    @Inject MangaSyncManager syncManager;
 
-    private MyAnimeList myAnimeList;
-    private Manga manga;
-    private ChapterSync chapterSync;
+    protected MyAnimeList myAnimeList;
+    protected Manga manga;
+    private MangaSync mangaSync;
 
     private String query;
 
@@ -37,15 +38,19 @@ public class MyAnimeListPresenter extends BasePresenter<MyAnimeListFragment> {
     protected void onCreate(Bundle savedState) {
         super.onCreate(savedState);
 
+        if (savedState != null) {
+            onProcessRestart();
+        }
+
         myAnimeList = syncManager.getMyAnimeList();
 
         restartableLatestCache(GET_CHAPTER_SYNC,
-                () -> db.getChapterSync(manga, myAnimeList).createObservable()
+                () -> db.getMangaSync(manga, myAnimeList).createObservable()
                         .flatMap(Observable::from)
-                        .doOnNext(chapterSync -> this.chapterSync = chapterSync)
+                        .doOnNext(mangaSync -> this.mangaSync = mangaSync)
                         .subscribeOn(Schedulers.io())
                         .observeOn(AndroidSchedulers.mainThread()),
-                MyAnimeListFragment::setChapterSync);
+                MyAnimeListFragment::setMangaSync);
 
         restartableLatestCache(GET_SEARCH_RESULTS,
                 () -> myAnimeList.search(query)
@@ -59,6 +64,11 @@ public class MyAnimeListPresenter extends BasePresenter<MyAnimeListFragment> {
 
     }
 
+    private void onProcessRestart() {
+        stop(GET_CHAPTER_SYNC);
+        stop(GET_SEARCH_RESULTS);
+    }
+
     @Override
     protected void onTakeView(MyAnimeListFragment view) {
         super.onTakeView(view);
@@ -81,10 +91,10 @@ public class MyAnimeListPresenter extends BasePresenter<MyAnimeListFragment> {
         if (updateSubscription != null)
             remove(updateSubscription);
 
-        chapterSync.last_chapter_read = chapterNumber;
+        mangaSync.last_chapter_read = chapterNumber;
 
-        add(updateSubscription = myAnimeList.update(chapterSync)
-                .flatMap(response -> db.insertChapterSync(chapterSync).createObservable())
+        add(updateSubscription = myAnimeList.update(mangaSync)
+                .flatMap(response -> db.insertMangaSync(mangaSync).createObservable())
                 .subscribeOn(Schedulers.io())
                 .observeOn(AndroidSchedulers.mainThread())
                 .subscribe(response -> {},
@@ -99,8 +109,19 @@ public class MyAnimeListPresenter extends BasePresenter<MyAnimeListFragment> {
         start(GET_SEARCH_RESULTS);
     }
 
-    public void registerManga(ChapterSync selectedManga) {
-        selectedManga.manga_id = manga.id;
-        db.insertChapterSync(selectedManga).executeAsBlocking();
+    public void registerManga(MangaSync manga) {
+        manga.manga_id = this.manga.id;
+        add(myAnimeList.bind(manga)
+                .flatMap(response -> {
+                    if (response.code() == 200 || response.code() == 201)
+                        return Observable.just(manga);
+                    return Observable.error(new Exception("Could not add manga"));
+                })
+                .flatMap(manga2 -> db.insertMangaSync(manga2).createObservable())
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(manga2 -> {},
+                        error -> ToastUtil.showShort(getContext(), error.getMessage())));
     }
+
 }

+ 14 - 14
app/src/main/java/eu/kanade/mangafeed/ui/reader/ReaderPresenter.java

@@ -8,12 +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.mangasync.MangaSyncManager;
+import eu.kanade.mangafeed.data.mangasync.services.MyAnimeList;
+import eu.kanade.mangafeed.data.sync.UpdateMangaSyncService;
 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.MangaSync;
 import eu.kanade.mangafeed.data.database.models.Manga;
 import eu.kanade.mangafeed.data.download.DownloadManager;
 import eu.kanade.mangafeed.data.preference.PreferencesHelper;
@@ -21,7 +21,7 @@ import eu.kanade.mangafeed.data.source.SourceManager;
 import eu.kanade.mangafeed.data.source.base.Source;
 import eu.kanade.mangafeed.data.source.model.Page;
 import eu.kanade.mangafeed.event.ReaderEvent;
-import eu.kanade.mangafeed.event.UpdateChapterSyncEvent;
+import eu.kanade.mangafeed.event.UpdateMangaSyncEvent;
 import eu.kanade.mangafeed.ui.base.presenter.BasePresenter;
 import eu.kanade.mangafeed.util.EventBusHook;
 import icepick.State;
@@ -37,7 +37,7 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
     @Inject PreferencesHelper prefs;
     @Inject DatabaseHelper db;
     @Inject DownloadManager downloadManager;
-    @Inject ChapterSyncManager syncManager;
+    @Inject MangaSyncManager syncManager;
     @Inject SourceManager sourceManager;
 
     @State Manga manga;
@@ -235,7 +235,7 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
         chapter.last_page_read = currentPage;
         if (isChapterFinished()) {
             chapter.read = true;
-            updateChapterSyncLastChapterRead();
+            updateMangaSyncLastChapterRead();
         }
         db.insertChapter(chapter).executeAsBlocking();
     }
@@ -245,26 +245,26 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
         return !chapter.read && currentPage == pageList.size() - 1;
     }
 
-    private void updateChapterSyncLastChapterRead() {
+    private void updateMangaSyncLastChapterRead() {
         // TODO don't use MAL methods for possible alternatives to MAL
         MyAnimeList mal = syncManager.getMyAnimeList();
 
         if (!mal.isLogged())
             return;
 
-        List<ChapterSync> result = db.getChapterSync(manga, mal).executeAsBlocking();
+        List<MangaSync> result = db.getMangaSync(manga, mal).executeAsBlocking();
         if (result.isEmpty())
             return;
 
-        ChapterSync chapterSync = result.get(0);
+        MangaSync mangaSync = result.get(0);
 
         int lastChapterReadLocal = (int) Math.floor(chapter.chapter_number);
-        int lastChapterReadRemote = chapterSync.last_chapter_read;
+        int lastChapterReadRemote = mangaSync.last_chapter_read;
 
         if (lastChapterReadLocal > lastChapterReadRemote) {
-            chapterSync.last_chapter_read = lastChapterReadLocal;
-            EventBus.getDefault().postSticky(new UpdateChapterSyncEvent(chapterSync));
-            UpdateChapterSyncService.start(getContext());
+            mangaSync.last_chapter_read = lastChapterReadLocal;
+            EventBus.getDefault().postSticky(new UpdateMangaSyncEvent(mangaSync));
+            UpdateMangaSyncService.start(getContext());
         }
     }
 

+ 10 - 10
app/src/main/java/eu/kanade/mangafeed/ui/setting/SettingsAccountsFragment.java

@@ -12,18 +12,18 @@ import java.util.List;
 import javax.inject.Inject;
 
 import eu.kanade.mangafeed.App;
-import eu.kanade.mangafeed.data.chaptersync.BaseChapterSync;
-import eu.kanade.mangafeed.data.chaptersync.ChapterSyncManager;
+import eu.kanade.mangafeed.data.mangasync.base.BaseMangaSync;
+import eu.kanade.mangafeed.data.mangasync.MangaSyncManager;
 import eu.kanade.mangafeed.data.source.SourceManager;
 import eu.kanade.mangafeed.data.source.base.Source;
-import eu.kanade.mangafeed.ui.setting.preference.ChapterSyncLoginDialog;
+import eu.kanade.mangafeed.ui.setting.preference.MangaSyncLoginDialog;
 import eu.kanade.mangafeed.ui.setting.preference.SourceLoginDialog;
 import rx.Observable;
 
 public class SettingsAccountsFragment extends SettingsNestedFragment {
 
     @Inject SourceManager sourceManager;
-    @Inject ChapterSyncManager syncManager;
+    @Inject MangaSyncManager syncManager;
 
     public static SettingsNestedFragment newInstance(int resourcePreference, int resourceTitle) {
         SettingsNestedFragment fragment = new SettingsAccountsFragment();
@@ -56,16 +56,16 @@ public class SettingsAccountsFragment extends SettingsNestedFragment {
             sourceCategory.addPreference(dialog);
         }
 
-        PreferenceCategory chapterSyncCategory = new PreferenceCategory(screen.getContext());
-        chapterSyncCategory.setTitle("Sync");
-        screen.addPreference(chapterSyncCategory);
+        PreferenceCategory mangaSyncCategory = new PreferenceCategory(screen.getContext());
+        mangaSyncCategory.setTitle("Sync");
+        screen.addPreference(mangaSyncCategory);
 
-        for (BaseChapterSync sync : syncManager.getChapterSyncServices()) {
-            ChapterSyncLoginDialog dialog = new ChapterSyncLoginDialog(
+        for (BaseMangaSync sync : syncManager.getSyncServices()) {
+            MangaSyncLoginDialog dialog = new MangaSyncLoginDialog(
                     screen.getContext(), preferences, sync);
             dialog.setTitle(sync.getName());
 
-            chapterSyncCategory.addPreference(dialog);
+            mangaSyncCategory.addPreference(dialog);
         }
 
         return view;

+ 8 - 8
app/src/main/java/eu/kanade/mangafeed/ui/setting/preference/ChapterSyncLoginDialog.java → app/src/main/java/eu/kanade/mangafeed/ui/setting/preference/MangaSyncLoginDialog.java

@@ -5,17 +5,17 @@ import android.content.DialogInterface;
 import android.view.View;
 
 import eu.kanade.mangafeed.R;
-import eu.kanade.mangafeed.data.chaptersync.BaseChapterSync;
+import eu.kanade.mangafeed.data.mangasync.base.BaseMangaSync;
 import eu.kanade.mangafeed.data.preference.PreferencesHelper;
 import eu.kanade.mangafeed.util.ToastUtil;
 import rx.android.schedulers.AndroidSchedulers;
 import rx.schedulers.Schedulers;
 
-public class ChapterSyncLoginDialog extends LoginDialogPreference {
+public class MangaSyncLoginDialog extends LoginDialogPreference {
 
-    private BaseChapterSync sync;
+    private BaseMangaSync sync;
 
-    public ChapterSyncLoginDialog(Context context, PreferencesHelper preferences, BaseChapterSync sync) {
+    public MangaSyncLoginDialog(Context context, PreferencesHelper preferences, BaseMangaSync sync) {
         super(context, preferences);
         this.sync = sync;
     }
@@ -26,8 +26,8 @@ public class ChapterSyncLoginDialog extends LoginDialogPreference {
 
         title.setText(getContext().getString(R.string.accounts_login_title, sync.getName()));
 
-        username.setText(preferences.getChapterSyncUsername(sync));
-        password.setText(preferences.getChapterSyncPassword(sync));
+        username.setText(preferences.getMangaSyncUsername(sync));
+        password.setText(preferences.getMangaSyncPassword(sync));
     }
 
     @Override
@@ -35,7 +35,7 @@ public class ChapterSyncLoginDialog extends LoginDialogPreference {
         super.onDialogClosed(positiveResult);
 
         if (positiveResult) {
-            preferences.setChapterSyncCredentials(sync,
+            preferences.setMangaSyncCredentials(sync,
                     username.getText().toString(),
                     password.getText().toString());
         }
@@ -61,7 +61,7 @@ public class ChapterSyncLoginDialog extends LoginDialogPreference {
                         dialog.dismiss();
                         ToastUtil.showShort(context, R.string.login_success);
                     } else {
-                        preferences.setChapterSyncCredentials(sync, "", "");
+                        preferences.setMangaSyncCredentials(sync, "", "");
                         loginBtn.setProgress(-1);
                     }
                 }, error -> {

+ 36 - 1
app/src/main/res/layout/fragment_myanimelist.xml

@@ -35,7 +35,7 @@
             android:layout_height="wrap_content"
             android:inputType="number"
             android:ems="10"
-            android:id="@+id/myanimelist_last_chapter_read"/>
+            android:id="@+id/last_chapter_read"/>
 
         <Button
             android:layout_width="wrap_content"
@@ -45,5 +45,40 @@
 
     </LinearLayout>
 
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content">
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Score"
+            android:layout_marginRight="10dp"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:id="@+id/score"/>
+
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content">
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Status"
+            android:layout_marginRight="10dp"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:id="@+id/status"/>
+
+    </LinearLayout>
+
 
 </LinearLayout>

+ 1 - 1
app/src/main/res/values/arrays.xml

@@ -22,7 +22,7 @@
         <item>@string/webtoon_viewer</item>
     </string-array>
 
-    <string-array name="download_threads">
+    <string-array name="download_slots">
         <item>1</item>
         <item>2</item>
         <item>3</item>

+ 1 - 1
app/src/main/res/values/keys.xml

@@ -16,7 +16,7 @@
     <string name="pref_custom_brightness_value_key">pref_custom_brightness_value_key</string>
 
     <string name="pref_download_directory_key">pref_download_directory_key</string>
-    <string name="pref_download_threads_key">pref_download_threads_key</string>
+    <string name="pref_download_slots_key">pref_download_slots_key</string>
 
     <string name="pref_chapter_cache_size_key">pref_chapter_cache_size_key</string>
     <string name="pref_clear_chapter_cache_key">pref_clear_chapter_cache_key</string>

+ 1 - 1
app/src/main/res/values/strings.xml

@@ -56,7 +56,7 @@
 
       <!-- Downloads section -->
     <string name="pref_download_directory">Downloads directory</string>
-    <string name="pref_download_threads">Download threads</string>
+    <string name="pref_download_slots">Simultaneous downloads</string>
 
       <!-- Cache section -->
     <string name="pref_chapter_cache_size">Chapters cache size</string>

+ 5 - 5
app/src/main/res/xml/pref_downloads.xml

@@ -5,11 +5,11 @@
         android:title="@string/pref_download_directory"
         android:key="@string/pref_download_directory_key"/>
 
-    <ListPreference
-        android:title="@string/pref_download_threads"
-        android:key="@string/pref_download_threads_key"
-        android:entries="@array/download_threads"
-        android:entryValues="@array/download_threads"
+    <eu.kanade.mangafeed.ui.setting.preference.IntListPreference
+        android:title="@string/pref_download_slots"
+        android:key="@string/pref_download_slots_key"
+        android:entries="@array/download_slots"
+        android:entryValues="@array/download_slots"
         android:defaultValue="1"
         android:summary="%s"/>