mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-04 08:08:55 +01:00 
			
		
		
		
	Improve MAL support (UI is very simple yet).
This commit is contained in:
		@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
}
 | 
			
		||||
@@ -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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -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,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
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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"
 | 
			
		||||
                + ");";
 | 
			
		||||
@@ -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;
 | 
			
		||||
@@ -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);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -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();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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 -> {
 | 
			
		||||
@@ -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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -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();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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++;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -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())));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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 -> {
 | 
			
		||||
@@ -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>
 | 
			
		||||
@@ -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>
 | 
			
		||||
 
 | 
			
		||||
@@ -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>
 | 
			
		||||
 
 | 
			
		||||
@@ -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,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"/>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user