diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/MangaSync.java b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/MangaSync.java index 7e7ee1b7f..0e3beb98e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/MangaSync.java +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/MangaSync.java @@ -6,7 +6,7 @@ import com.pushtorefresh.storio.sqlite.annotations.StorIOSQLiteType; import java.io.Serializable; import eu.kanade.tachiyomi.data.database.tables.MangaSyncTable; -import eu.kanade.tachiyomi.data.mangasync.base.MangaSyncService; +import eu.kanade.tachiyomi.data.mangasync.MangaSyncService; @StorIOSQLiteType(table = MangaSyncTable.TABLE) public class MangaSync implements Serializable { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/MangaSyncQueries.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/MangaSyncQueries.kt index 2119dfde9..5140b516e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/MangaSyncQueries.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/MangaSyncQueries.kt @@ -6,7 +6,7 @@ import eu.kanade.tachiyomi.data.database.DbProvider import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.MangaSync import eu.kanade.tachiyomi.data.database.tables.MangaSyncTable -import eu.kanade.tachiyomi.data.mangasync.base.MangaSyncService +import eu.kanade.tachiyomi.data.mangasync.MangaSyncService interface MangaSyncQueries : DbProvider { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/mangasync/MangaSyncManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/mangasync/MangaSyncManager.kt index 54a29975a..220e75140 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/mangasync/MangaSyncManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/mangasync/MangaSyncManager.kt @@ -1,23 +1,18 @@ package eu.kanade.tachiyomi.data.mangasync import android.content.Context -import eu.kanade.tachiyomi.data.mangasync.base.MangaSyncService -import eu.kanade.tachiyomi.data.mangasync.services.MyAnimeList +import eu.kanade.tachiyomi.data.mangasync.myanimelist.MyAnimeList class MangaSyncManager(private val context: Context) { - val services: List - val myAnimeList: MyAnimeList - companion object { const val MYANIMELIST = 1 } - init { - myAnimeList = MyAnimeList(context, MYANIMELIST) - services = listOf(myAnimeList) - } + val myAnimeList = MyAnimeList(context, MYANIMELIST) - fun getService(id: Int): MangaSyncService = services.find { it.id == id }!! + val services = listOf(myAnimeList) + + fun getService(id: Int) = services.find { it.id == id } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/mangasync/MangaSyncService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/mangasync/MangaSyncService.kt new file mode 100644 index 000000000..c5d8dbd26 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/mangasync/MangaSyncService.kt @@ -0,0 +1,56 @@ +package eu.kanade.tachiyomi.data.mangasync + +import android.content.Context +import android.support.annotation.CallSuper +import eu.kanade.tachiyomi.App +import eu.kanade.tachiyomi.data.database.models.MangaSync +import eu.kanade.tachiyomi.data.network.NetworkHelper +import eu.kanade.tachiyomi.data.preference.PreferencesHelper +import okhttp3.OkHttpClient +import rx.Completable +import rx.Observable +import javax.inject.Inject + +abstract class MangaSyncService(private val context: Context, val id: Int) { + + @Inject lateinit var preferences: PreferencesHelper + @Inject lateinit var networkService: NetworkHelper + + init { + App.get(context).component.inject(this) + } + + open val client: OkHttpClient + get() = networkService.client + + // Name of the manga sync service to display + abstract val name: String + + abstract fun login(username: String, password: String): Completable + + open val isLogged: Boolean + get() = !getUsername().isEmpty() && + !getPassword().isEmpty() + + abstract fun add(manga: MangaSync): Observable + + abstract fun update(manga: MangaSync): Observable + + abstract fun bind(manga: MangaSync): Observable + + abstract fun getStatus(status: Int): String + + fun saveCredentials(username: String, password: String) { + preferences.setMangaSyncCredentials(this, username, password) + } + + @CallSuper + open fun logout() { + preferences.setMangaSyncCredentials(this, "", "") + } + + fun getUsername() = preferences.mangaSyncUsername(this) + + fun getPassword() = preferences.mangaSyncPassword(this) + +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/mangasync/UpdateMangaSyncService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/mangasync/UpdateMangaSyncService.kt index 81f0c7459..b11ba7546 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/mangasync/UpdateMangaSyncService.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/mangasync/UpdateMangaSyncService.kt @@ -48,15 +48,13 @@ class UpdateMangaSyncService : Service() { private fun updateLastChapterRead(mangaSync: MangaSync, startId: Int) { val sync = syncManager.getService(mangaSync.sync_id) + if (sync == null) { + stopSelf(startId) + return + } subscriptions.add(Observable.defer { sync.update(mangaSync) } - .flatMap { - if (it.isSuccessful) { - db.insertMangaSync(mangaSync).asRxObservable() - } else { - Observable.error(Exception("Could not update manga in remote service")) - } - } + .flatMap { db.insertMangaSync(mangaSync).asRxObservable() } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe({ stopSelf(startId) }, diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/mangasync/base/MangaSyncService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/mangasync/base/MangaSyncService.kt deleted file mode 100644 index 10dc26086..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/data/mangasync/base/MangaSyncService.kt +++ /dev/null @@ -1,42 +0,0 @@ -package eu.kanade.tachiyomi.data.mangasync.base - -import android.content.Context -import eu.kanade.tachiyomi.App -import eu.kanade.tachiyomi.data.database.models.MangaSync -import eu.kanade.tachiyomi.data.network.NetworkHelper -import eu.kanade.tachiyomi.data.preference.PreferencesHelper -import okhttp3.OkHttpClient -import okhttp3.Response -import rx.Observable -import javax.inject.Inject - -abstract class MangaSyncService(private val context: Context, val id: Int) { - - @Inject lateinit var preferences: PreferencesHelper - @Inject lateinit var networkService: NetworkHelper - - init { - App.get(context).component.inject(this) - } - - open val client: OkHttpClient - get() = networkService.client - - // Name of the manga sync service to display - abstract val name: String - - abstract fun login(username: String, password: String): Observable - - open val isLogged: Boolean - get() = !preferences.mangaSyncUsername(this).isEmpty() && - !preferences.mangaSyncPassword(this).isEmpty() - - abstract fun update(manga: MangaSync): Observable - - abstract fun add(manga: MangaSync): Observable - - abstract fun bind(manga: MangaSync): Observable - - abstract fun getStatus(status: Int): String - -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/mangasync/services/MyAnimeList.kt b/app/src/main/java/eu/kanade/tachiyomi/data/mangasync/myanimelist/MyAnimeList.kt similarity index 57% rename from app/src/main/java/eu/kanade/tachiyomi/data/mangasync/services/MyAnimeList.kt rename to app/src/main/java/eu/kanade/tachiyomi/data/mangasync/myanimelist/MyAnimeList.kt index 29ed4ac40..29e08da1d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/mangasync/services/MyAnimeList.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/mangasync/myanimelist/MyAnimeList.kt @@ -1,26 +1,29 @@ -package eu.kanade.tachiyomi.data.mangasync.services +package eu.kanade.tachiyomi.data.mangasync.myanimelist import android.content.Context import android.net.Uri import android.util.Xml import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.MangaSync -import eu.kanade.tachiyomi.data.mangasync.base.MangaSyncService +import eu.kanade.tachiyomi.data.mangasync.MangaSyncService import eu.kanade.tachiyomi.data.network.GET import eu.kanade.tachiyomi.data.network.POST import eu.kanade.tachiyomi.data.network.asObservable import eu.kanade.tachiyomi.util.selectInt import eu.kanade.tachiyomi.util.selectText -import okhttp3.* +import okhttp3.Credentials +import okhttp3.FormBody +import okhttp3.Headers +import okhttp3.RequestBody import org.jsoup.Jsoup import org.xmlpull.v1.XmlSerializer +import rx.Completable import rx.Observable import java.io.StringWriter class MyAnimeList(private val context: Context, id: Int) : MangaSyncService(context, id) { private lateinit var headers: Headers - private lateinit var username: String companion object { val BASE_URL = "http://myanimelist.net" @@ -41,8 +44,8 @@ class MyAnimeList(private val context: Context, id: Int) : MangaSyncService(cont } init { - val username = preferences.mangaSyncUsername(this) - val password = preferences.mangaSyncPassword(this) + val username = getUsername() + val password = getPassword() if (!username.isEmpty() && !password.isEmpty()) { createHeaders(username, password) @@ -52,25 +55,39 @@ class MyAnimeList(private val context: Context, id: Int) : MangaSyncService(cont override val name: String get() = "MyAnimeList" - fun getLoginUrl(): String { - return Uri.parse(BASE_URL).buildUpon() - .appendEncodedPath("api/account/verify_credentials.xml") - .toString() - } + fun getLoginUrl() = Uri.parse(BASE_URL).buildUpon() + .appendEncodedPath("api/account/verify_credentials.xml") + .toString() - override fun login(username: String, password: String): Observable { + fun getSearchUrl(query: String) = Uri.parse(BASE_URL).buildUpon() + .appendEncodedPath("api/manga/search.xml") + .appendQueryParameter("q", query) + .toString() + + fun getListUrl(username: String) = Uri.parse(BASE_URL).buildUpon() + .appendPath("malappinfo.php") + .appendQueryParameter("u", username) + .appendQueryParameter("status", "all") + .appendQueryParameter("type", "manga") + .toString() + + fun getUpdateUrl(manga: MangaSync) = Uri.parse(BASE_URL).buildUpon() + .appendEncodedPath("api/mangalist/update") + .appendPath("${manga.remote_id}.xml") + .toString() + + fun getAddUrl(manga: MangaSync) = Uri.parse(BASE_URL).buildUpon() + .appendEncodedPath("api/mangalist/add") + .appendPath("${manga.remote_id}.xml") + .toString() + + override fun login(username: String, password: String): Completable { createHeaders(username, password) return client.newCall(GET(getLoginUrl(), headers)) .asObservable() .doOnNext { it.close() } - .map { it.code() == 200 } - } - - fun getSearchUrl(query: String): String { - return Uri.parse(BASE_URL).buildUpon() - .appendEncodedPath("api/manga/search.xml") - .appendQueryParameter("q", query) - .toString() + .doOnNext { if (it.code() != 200) throw Exception("Login error") } + .toCompletable() } fun search(query: String): Observable> { @@ -80,73 +97,56 @@ class MyAnimeList(private val context: Context, id: Int) : MangaSyncService(cont .flatMap { Observable.from(it.select("entry")) } .filter { it.select("type").text() != "Novel" } .map { - val manga = MangaSync.create(this) - manga.title = it.selectText("title") - manga.remote_id = it.selectInt("id") - manga.total_chapters = it.selectInt("chapters") - manga + MangaSync.create(this).apply { + title = it.selectText("title") + remote_id = it.selectInt("id") + total_chapters = it.selectInt("chapters") + } } .toList() } - fun getListUrl(username: String): String { - return Uri.parse(BASE_URL).buildUpon() - .appendPath("malappinfo.php") - .appendQueryParameter("u", username) - .appendQueryParameter("status", "all") - .appendQueryParameter("type", "manga") - .toString() - } - // MAL doesn't support score with decimals fun getList(): Observable> { return networkService.forceCacheClient - .newCall(GET(getListUrl(username), headers)) + .newCall(GET(getListUrl(getUsername()), headers)) .asObservable() .map { Jsoup.parse(it.body().string()) } .flatMap { Observable.from(it.select("manga")) } .map { - val manga = MangaSync.create(this) - manga.title = it.selectText("series_title") - manga.remote_id = it.selectInt("series_mangadb_id") - manga.last_chapter_read = it.selectInt("my_read_chapters") - manga.status = it.selectInt("my_status") - manga.score = it.selectInt("my_score").toFloat() - manga.total_chapters = it.selectInt("series_chapters") - manga + MangaSync.create(this).apply { + title = it.selectText("series_title") + remote_id = it.selectInt("series_mangadb_id") + last_chapter_read = it.selectInt("my_read_chapters") + status = it.selectInt("my_status") + score = it.selectInt("my_score").toFloat() + total_chapters = it.selectInt("series_chapters") + } } .toList() } - fun getUpdateUrl(manga: MangaSync): String { - return Uri.parse(BASE_URL).buildUpon() - .appendEncodedPath("api/mangalist/update") - .appendPath(manga.remote_id.toString() + ".xml") - .toString() - } - - override fun update(manga: MangaSync): Observable { + override fun update(manga: MangaSync): Observable { return Observable.defer { if (manga.total_chapters != 0 && manga.last_chapter_read == manga.total_chapters) { manga.status = COMPLETED } client.newCall(POST(getUpdateUrl(manga), headers, getMangaPostPayload(manga))) .asObservable() + .doOnNext { it.close() } + .doOnNext { if (!it.isSuccessful) throw Exception("Could not update manga") } + .map { manga } } } - fun getAddUrl(manga: MangaSync): String { - return Uri.parse(BASE_URL).buildUpon() - .appendEncodedPath("api/mangalist/add") - .appendPath(manga.remote_id.toString() + ".xml") - .toString() - } - - override fun add(manga: MangaSync): Observable { + override fun add(manga: MangaSync): Observable { return Observable.defer { client.newCall(POST(getAddUrl(manga), headers, getMangaPostPayload(manga))) .asObservable() + .doOnNext { it.close() } + .doOnNext { if (!it.isSuccessful) throw Exception("Could not add manga") } + .map { manga } } } @@ -184,21 +184,20 @@ class MyAnimeList(private val context: Context, id: Int) : MangaSyncService(cont endTag(namespace, tag) } - override fun bind(manga: MangaSync): Observable { + override fun bind(manga: MangaSync): Observable { return getList() - .flatMap { + .flatMap { userlist -> manga.sync_id = id - for (remoteManga in it) { - if (remoteManga.remote_id == manga.remote_id) { - // Manga is already in the list - manga.copyPersonalFrom(remoteManga) - return@flatMap update(manga) - } + val mangaFromList = userlist.find { it.remote_id == manga.remote_id } + if (mangaFromList != null) { + manga.copyPersonalFrom(mangaFromList) + update(manga) + } else { + // Set default fields if it's not found in the list + manga.score = DEFAULT_SCORE.toFloat() + manga.status = DEFAULT_STATUS + add(manga) } - // Set default fields if it's not found in the list - manga.score = DEFAULT_SCORE.toFloat() - manga.status = DEFAULT_STATUS - return@flatMap add(manga) } } @@ -214,7 +213,6 @@ class MyAnimeList(private val context: Context, id: Int) : MangaSyncService(cont } fun createHeaders(username: String, password: String) { - this.username = username val builder = Headers.Builder() builder.add("Authorization", Credentials.basic(username, password)) builder.add("User-Agent", "api-indiv-9F93C52A963974CF674325391990191C") diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt index a0813c7a9..4e31a5c04 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt @@ -6,7 +6,7 @@ import android.preference.PreferenceManager import com.f2prateek.rx.preferences.Preference import com.f2prateek.rx.preferences.RxSharedPreferences import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.mangasync.base.MangaSyncService +import eu.kanade.tachiyomi.data.mangasync.MangaSyncService import eu.kanade.tachiyomi.data.source.Source import java.io.File import java.io.IOException diff --git a/app/src/main/java/eu/kanade/tachiyomi/injection/component/AppComponent.kt b/app/src/main/java/eu/kanade/tachiyomi/injection/component/AppComponent.kt index d7862d3a3..4856e8b12 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/injection/component/AppComponent.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/injection/component/AppComponent.kt @@ -6,8 +6,8 @@ import eu.kanade.tachiyomi.data.download.DownloadService import eu.kanade.tachiyomi.data.glide.AppGlideModule import eu.kanade.tachiyomi.data.glide.MangaModelLoader import eu.kanade.tachiyomi.data.library.LibraryUpdateService +import eu.kanade.tachiyomi.data.mangasync.MangaSyncService import eu.kanade.tachiyomi.data.mangasync.UpdateMangaSyncService -import eu.kanade.tachiyomi.data.mangasync.base.MangaSyncService import eu.kanade.tachiyomi.data.source.Source import eu.kanade.tachiyomi.data.source.online.OnlineSource import eu.kanade.tachiyomi.data.updater.UpdateDownloader diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/myanimelist/MyAnimeListPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/myanimelist/MyAnimeListPresenter.kt index d6ad8ca87..af5b380d6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/myanimelist/MyAnimeListPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/myanimelist/MyAnimeListPresenter.kt @@ -98,7 +98,7 @@ class MyAnimeListPresenter : BasePresenter() { mangaSync?.let { mangaSync -> add(myAnimeList.update(mangaSync) .subscribeOn(Schedulers.io()) - .flatMap { response -> db.insertMangaSync(mangaSync).asRxObservable() } + .flatMap { db.insertMangaSync(mangaSync).asRxObservable() } .observeOn(AndroidSchedulers.mainThread()) .subscribe({ next -> }, { error -> @@ -126,13 +126,7 @@ class MyAnimeListPresenter : BasePresenter() { if (sync != null) { sync.manga_id = manga.id add(myAnimeList.bind(sync) - .flatMap { response -> - if (response.isSuccessful) { - db.insertMangaSync(sync).asRxObservable() - } else { - Observable.error(Exception("Could not bind manga")) - } - } + .flatMap { db.insertMangaSync(sync).asRxObservable() } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe({ }, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt index e8923c6d3..a5518baa4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt @@ -374,7 +374,7 @@ class ReaderPresenter : BasePresenter() { fun updateMangaSyncLastChapterRead() { for (mangaSync in mangaSyncList ?: emptyList()) { - val service = syncManager.getService(mangaSync.sync_id) + val service = syncManager.getService(mangaSync.sync_id) ?: continue if (service.isLogged && mangaSync.update) { UpdateMangaSyncService.start(context, mangaSync) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/preference/MangaSyncLoginDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/preference/MangaSyncLoginDialog.kt index 2379b6a13..53200eb68 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/preference/MangaSyncLoginDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/preference/MangaSyncLoginDialog.kt @@ -3,7 +3,7 @@ package eu.kanade.tachiyomi.widget.preference import android.os.Bundle import android.view.View import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.mangasync.base.MangaSyncService +import eu.kanade.tachiyomi.data.mangasync.MangaSyncService import eu.kanade.tachiyomi.ui.setting.SettingsActivity import eu.kanade.tachiyomi.util.toast import kotlinx.android.synthetic.main.pref_account_login.view.* @@ -29,13 +29,13 @@ class MangaSyncLoginDialog : LoginDialogPreference() { super.onCreate(savedInstanceState) val syncId = arguments.getInt("key") - sync = (activity as SettingsActivity).syncManager.getService(syncId) + sync = (activity as SettingsActivity).syncManager.getService(syncId)!! } override fun setCredentialsOnView(view: View) = with(view) { dialog_title.text = getString(R.string.login_title, sync.name) - username.setText(preferences.mangaSyncUsername(sync)) - password.setText(preferences.mangaSyncPassword(sync)) + username.setText(sync.getUsername()) + password.setText(sync.getPassword()) } override fun checkLogin() { @@ -46,25 +46,20 @@ class MangaSyncLoginDialog : LoginDialogPreference() { return login.progress = 1 + val user = username.text.toString() + val pass = password.text.toString() - requestSubscription = sync.login(username.text.toString(), password.text.toString()) + requestSubscription = sync.login(user, pass) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ logged -> - if (logged) { - preferences.setMangaSyncCredentials(sync, - username.text.toString(), - password.text.toString()) - - dialog.dismiss() - context.toast(R.string.login_success) - } else { - preferences.setMangaSyncCredentials(sync, "", "") - login.progress = -1 - } - }, { error -> + .subscribe({ error -> + sync.logout() login.progress = -1 login.setText(R.string.unknown_error) + }, { + sync.saveCredentials(user, pass) + dialog.dismiss() + context.toast(R.string.login_success) }) }