Improve MAL support (UI is very simple yet).

This commit is contained in:
inorichi 2015-12-14 13:24:05 +01:00
parent a1d67c0fce
commit 8dca7fe79a
30 changed files with 569 additions and 392 deletions

View File

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

View File

@ -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);
}

View File

@ -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;
}
}

View File

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

View File

@ -5,14 +5,14 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteOpenHelper;
import android.support.annotation.NonNull; 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.ChapterTable;
import eu.kanade.mangafeed.data.database.tables.MangaTable; import eu.kanade.mangafeed.data.database.tables.MangaTable;
public class DbOpenHelper extends SQLiteOpenHelper { public class DbOpenHelper extends SQLiteOpenHelper {
public static final String DATABASE_NAME = "mangafeed.db"; 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) { public DbOpenHelper(@NonNull Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION); super(context, DATABASE_NAME, null, DATABASE_VERSION);
@ -22,13 +22,13 @@ public class DbOpenHelper extends SQLiteOpenHelper {
public void onCreate(@NonNull SQLiteDatabase db) { public void onCreate(@NonNull SQLiteDatabase db) {
db.execSQL(MangaTable.getCreateTableQuery()); db.execSQL(MangaTable.getCreateTableQuery());
db.execSQL(ChapterTable.getCreateTableQuery()); db.execSQL(ChapterTable.getCreateTableQuery());
db.execSQL(ChapterSyncTable.getCreateTableQuery()); db.execSQL(MangaSyncTable.getCreateTableQuery());
} }
@Override @Override
public void onUpgrade(@NonNull SQLiteDatabase db, int oldVersion, int newVersion) { public void onUpgrade(@NonNull SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion == 1) if (oldVersion < 3)
db.execSQL(ChapterSyncTable.getCreateTableQuery()); db.execSQL(MangaSyncTable.getCreateTableQuery());
} }
@Override @Override

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

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

View File

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

View File

@ -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);
}

View File

@ -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;
}
}

View File

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

View File

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

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -5,8 +5,8 @@ import android.app.Application;
import javax.inject.Singleton; import javax.inject.Singleton;
import dagger.Component; import dagger.Component;
import eu.kanade.mangafeed.data.chaptersync.MyAnimeList; import eu.kanade.mangafeed.data.mangasync.services.MyAnimeList;
import eu.kanade.mangafeed.data.chaptersync.UpdateChapterSyncService; import eu.kanade.mangafeed.data.sync.UpdateMangaSyncService;
import eu.kanade.mangafeed.data.download.DownloadService; import eu.kanade.mangafeed.data.download.DownloadService;
import eu.kanade.mangafeed.data.source.base.Source; import eu.kanade.mangafeed.data.source.base.Source;
import eu.kanade.mangafeed.data.sync.LibraryUpdateService; import eu.kanade.mangafeed.data.sync.LibraryUpdateService;
@ -56,7 +56,7 @@ public interface AppComponent {
void inject(LibraryUpdateService libraryUpdateService); void inject(LibraryUpdateService libraryUpdateService);
void inject(DownloadService downloadService); void inject(DownloadService downloadService);
void inject(UpdateChapterSyncService updateChapterSyncService); void inject(UpdateMangaSyncService updateMangaSyncService);
Application application(); Application application();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,13 +4,14 @@ import android.os.Bundle;
import javax.inject.Inject; import javax.inject.Inject;
import eu.kanade.mangafeed.data.chaptersync.ChapterSyncManager; import eu.kanade.mangafeed.data.database.models.MangaSync;
import eu.kanade.mangafeed.data.chaptersync.MyAnimeList; 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.DatabaseHelper;
import eu.kanade.mangafeed.data.database.models.ChapterSync;
import eu.kanade.mangafeed.data.database.models.Manga; import eu.kanade.mangafeed.data.database.models.Manga;
import eu.kanade.mangafeed.ui.base.presenter.BasePresenter; import eu.kanade.mangafeed.ui.base.presenter.BasePresenter;
import eu.kanade.mangafeed.util.EventBusHook; import eu.kanade.mangafeed.util.EventBusHook;
import eu.kanade.mangafeed.util.ToastUtil;
import rx.Observable; import rx.Observable;
import rx.Subscription; import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers; import rx.android.schedulers.AndroidSchedulers;
@ -20,11 +21,11 @@ import timber.log.Timber;
public class MyAnimeListPresenter extends BasePresenter<MyAnimeListFragment> { public class MyAnimeListPresenter extends BasePresenter<MyAnimeListFragment> {
@Inject DatabaseHelper db; @Inject DatabaseHelper db;
@Inject ChapterSyncManager syncManager; @Inject MangaSyncManager syncManager;
private MyAnimeList myAnimeList; protected MyAnimeList myAnimeList;
private Manga manga; protected Manga manga;
private ChapterSync chapterSync; private MangaSync mangaSync;
private String query; private String query;
@ -37,15 +38,19 @@ public class MyAnimeListPresenter extends BasePresenter<MyAnimeListFragment> {
protected void onCreate(Bundle savedState) { protected void onCreate(Bundle savedState) {
super.onCreate(savedState); super.onCreate(savedState);
if (savedState != null) {
onProcessRestart();
}
myAnimeList = syncManager.getMyAnimeList(); myAnimeList = syncManager.getMyAnimeList();
restartableLatestCache(GET_CHAPTER_SYNC, restartableLatestCache(GET_CHAPTER_SYNC,
() -> db.getChapterSync(manga, myAnimeList).createObservable() () -> db.getMangaSync(manga, myAnimeList).createObservable()
.flatMap(Observable::from) .flatMap(Observable::from)
.doOnNext(chapterSync -> this.chapterSync = chapterSync) .doOnNext(mangaSync -> this.mangaSync = mangaSync)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()), .observeOn(AndroidSchedulers.mainThread()),
MyAnimeListFragment::setChapterSync); MyAnimeListFragment::setMangaSync);
restartableLatestCache(GET_SEARCH_RESULTS, restartableLatestCache(GET_SEARCH_RESULTS,
() -> myAnimeList.search(query) () -> 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 @Override
protected void onTakeView(MyAnimeListFragment view) { protected void onTakeView(MyAnimeListFragment view) {
super.onTakeView(view); super.onTakeView(view);
@ -81,10 +91,10 @@ public class MyAnimeListPresenter extends BasePresenter<MyAnimeListFragment> {
if (updateSubscription != null) if (updateSubscription != null)
remove(updateSubscription); remove(updateSubscription);
chapterSync.last_chapter_read = chapterNumber; mangaSync.last_chapter_read = chapterNumber;
add(updateSubscription = myAnimeList.update(chapterSync) add(updateSubscription = myAnimeList.update(mangaSync)
.flatMap(response -> db.insertChapterSync(chapterSync).createObservable()) .flatMap(response -> db.insertMangaSync(mangaSync).createObservable())
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(response -> {}, .subscribe(response -> {},
@ -99,8 +109,19 @@ public class MyAnimeListPresenter extends BasePresenter<MyAnimeListFragment> {
start(GET_SEARCH_RESULTS); start(GET_SEARCH_RESULTS);
} }
public void registerManga(ChapterSync selectedManga) { public void registerManga(MangaSync manga) {
selectedManga.manga_id = manga.id; manga.manga_id = this.manga.id;
db.insertChapterSync(selectedManga).executeAsBlocking(); 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())));
} }
} }

View File

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

View File

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

View File

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

View File

@ -35,7 +35,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="number" android:inputType="number"
android:ems="10" android:ems="10"
android:id="@+id/myanimelist_last_chapter_read"/> android:id="@+id/last_chapter_read"/>
<Button <Button
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -45,5 +45,40 @@
</LinearLayout> </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> </LinearLayout>

View File

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

View File

@ -16,7 +16,7 @@
<string name="pref_custom_brightness_value_key">pref_custom_brightness_value_key</string> <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_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_chapter_cache_size_key">pref_chapter_cache_size_key</string>
<string name="pref_clear_chapter_cache_key">pref_clear_chapter_cache_key</string> <string name="pref_clear_chapter_cache_key">pref_clear_chapter_cache_key</string>

View File

@ -56,7 +56,7 @@
<!-- Downloads section --> <!-- Downloads section -->
<string name="pref_download_directory">Downloads directory</string> <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 --> <!-- Cache section -->
<string name="pref_chapter_cache_size">Chapters cache size</string> <string name="pref_chapter_cache_size">Chapters cache size</string>

View File

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