Prechádzať zdrojové kódy

Improve MAL support (UI is very simple yet).

inorichi 9 rokov pred
rodič
commit
8dca7fe79a
30 zmenil súbory, kde vykonal 569 pridanie a 392 odobranie
  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"/>