From 80be0e403d77da54847e5cb2d0d6e7a32a8d5933 Mon Sep 17 00:00:00 2001 From: Zakhar Timoshenko Date: Mon, 30 Jan 2023 01:27:45 +0300 Subject: [PATCH] Update MAL codebase --- .../kotatsu/scrobbling/ScrobblingModule.kt | 13 ++++- .../scrobbling/mal/data/MALAuthenticator.kt | 5 +- .../scrobbling/mal/data/MALInterceptor.kt | 16 +++--- .../scrobbling/mal/data/MALRepository.kt | 57 ++++++++++++++----- .../kotatsu/scrobbling/mal/data/MALStorage.kt | 41 ------------- .../scrobbling/mal/data/model/MALUser.kt | 37 ------------ .../scrobbling/mal/domain/MALScrobbler.kt | 24 +------- .../scrobbling/mal/ui/MALSettingsFragment.kt | 4 +- .../scrobbling/mal/ui/MALSettingsViewModel.kt | 8 +-- .../settings/HistorySettingsFragment.kt | 2 +- app/src/main/res/xml/pref_history.xml | 1 - 11 files changed, 74 insertions(+), 134 deletions(-) delete mode 100644 app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/MALStorage.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/model/MALUser.kt diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/ScrobblingModule.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/ScrobblingModule.kt index cb85cfa08..6145227fc 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/ScrobblingModule.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/ScrobblingModule.kt @@ -20,7 +20,6 @@ import org.koitharu.kotatsu.scrobbling.domain.Scrobbler import org.koitharu.kotatsu.scrobbling.mal.data.MALAuthenticator import org.koitharu.kotatsu.scrobbling.mal.data.MALInterceptor import org.koitharu.kotatsu.scrobbling.mal.data.MALRepository -import org.koitharu.kotatsu.scrobbling.mal.data.MALStorage import org.koitharu.kotatsu.scrobbling.mal.domain.MALScrobbler import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerService import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerType @@ -54,13 +53,16 @@ object ScrobblingModule { @Provides @Singleton fun provideMALRepository( - storage: MALStorage, + @ScrobblerType(ScrobblerService.MAL) storage: ScrobblerStorage, database: MangaDatabase, authenticator: MALAuthenticator, ): MALRepository { val okHttp = OkHttpClient.Builder().apply { authenticator(authenticator) addInterceptor(MALInterceptor(storage)) + if (BuildConfig.DEBUG) { + addInterceptor(CurlLoggingInterceptor()) + } }.build() return MALRepository(okHttp, storage, database) } @@ -96,6 +98,13 @@ object ScrobblingModule { @ApplicationContext context: Context, ): ScrobblerStorage = ScrobblerStorage(context, ScrobblerService.SHIKIMORI) + @Provides + @Singleton + @ScrobblerType(ScrobblerService.MAL) + fun provideMALStorage( + @ApplicationContext context: Context, + ): ScrobblerStorage = ScrobblerStorage(context, ScrobblerService.MAL) + @Provides @ElementsIntoSet fun provideScrobblers( diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/MALAuthenticator.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/MALAuthenticator.kt index 5bef92286..c0c650c0a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/MALAuthenticator.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/MALAuthenticator.kt @@ -7,11 +7,14 @@ import okhttp3.Response import okhttp3.Route import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.core.network.CommonHeaders +import org.koitharu.kotatsu.scrobbling.data.ScrobblerStorage +import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerService +import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerType import javax.inject.Inject import javax.inject.Provider class MALAuthenticator @Inject constructor( - private val storage: MALStorage, + @ScrobblerType(ScrobblerService.MAL) private val storage: ScrobblerStorage, private val repositoryProvider: Provider, ) : Authenticator { diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/MALInterceptor.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/MALInterceptor.kt index 397877ee1..e7d28b261 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/MALInterceptor.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/MALInterceptor.kt @@ -3,23 +3,23 @@ package org.koitharu.kotatsu.scrobbling.mal.data import okhttp3.Interceptor import okhttp3.Response import org.koitharu.kotatsu.core.network.CommonHeaders -import java.io.IOException +import org.koitharu.kotatsu.scrobbling.data.ScrobblerStorage -class MALInterceptor(private val storage: MALStorage) : Interceptor { +private const val JSON = "application/json" + +class MALInterceptor(private val storage: ScrobblerStorage) : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val sourceRequest = chain.request() val request = sourceRequest.newBuilder() - if (!sourceRequest.url.pathSegments.contains("oauth2")) { + request.header(CommonHeaders.CONTENT_TYPE, JSON) + request.header(CommonHeaders.ACCEPT, JSON) + if (!sourceRequest.url.pathSegments.contains("oauth")) { storage.accessToken?.let { request.header(CommonHeaders.AUTHORIZATION, "Bearer $it") } } - val response = chain.proceed(request.build()) - if (!response.isSuccessful && !response.isRedirect) { - throw IOException("${response.code} ${response.message}") - } - return response + return chain.proceed(request.build()) } } diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/MALRepository.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/MALRepository.kt index 3dca6ecc0..af423d496 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/MALRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/MALRepository.kt @@ -3,11 +3,17 @@ package org.koitharu.kotatsu.scrobbling.mal.data import okhttp3.FormBody import okhttp3.OkHttpClient import okhttp3.Request +import org.json.JSONObject import org.koitharu.kotatsu.core.db.MangaDatabase +import org.koitharu.kotatsu.parsers.model.MangaChapter import org.koitharu.kotatsu.parsers.util.await import org.koitharu.kotatsu.parsers.util.parseJson +import org.koitharu.kotatsu.scrobbling.data.ScrobblerRepository +import org.koitharu.kotatsu.scrobbling.data.ScrobblerStorage +import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerManga +import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerMangaInfo import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerService -import org.koitharu.kotatsu.scrobbling.mal.data.model.MALUser +import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerUser import org.koitharu.kotatsu.utils.PKCEGenerator private const val REDIRECT_URI = "kotatsu://mal-auth" @@ -19,13 +25,13 @@ private const val MANGA_PAGE_SIZE = 250 class MALRepository( private val okHttp: OkHttpClient, - private val storage: MALStorage, + private val storage: ScrobblerStorage, private val db: MangaDatabase, -) { +) : ScrobblerRepository { private var codeVerifier: String = "" - val oauthUrl: String + override val oauthUrl: String get() = "${BASE_OAUTH_URL}/v1/oauth2/authorize?" + "response_type=code" + "&client_id=af16954886b040673378423f5d62cccd" + @@ -33,10 +39,12 @@ class MALRepository( "&code_challenge=${getPKCEChallengeCode()}" + "&code_challenge_method=plain" - val isAuthorized: Boolean + override val isAuthorized: Boolean get() = storage.accessToken != null + override val cachedUser: ScrobblerUser? + get() = TODO("Not yet implemented") - suspend fun authorize(code: String?) { + override suspend fun authorize(code: String?) { val body = FormBody.Builder() if (code != null) { body.add("client_id", "af16954886b040673378423f5d62cccd") @@ -53,7 +61,7 @@ class MALRepository( storage.refreshToken = response.getString("refresh_token") } - suspend fun loadUser(): MALUser { + override suspend fun loadUser(): ScrobblerUser { val request = Request.Builder() .get() .url("${BASE_API_URL}/users") @@ -61,15 +69,31 @@ class MALRepository( return MALUser(response).also { storage.user = it } } - fun getCachedUser(): MALUser? { - return storage.user - } - - suspend fun unregister(mangaId: Long) { + override suspend fun unregister(mangaId: Long) { return db.scrobblingDao.delete(ScrobblerService.MAL.id, mangaId) } - fun logout() { + override suspend fun findManga(query: String, offset: Int): List { + TODO("Not yet implemented") + } + + override suspend fun getMangaInfo(id: Long): ScrobblerMangaInfo { + TODO("Not yet implemented") + } + + override suspend fun createRate(mangaId: Long, scrobblerMangaId: Long) { + TODO("Not yet implemented") + } + + override suspend fun updateRate(rateId: Int, mangaId: Long, chapter: MangaChapter) { + TODO("Not yet implemented") + } + + override suspend fun updateRate(rateId: Int, mangaId: Long, rating: Float, status: String?, comment: String?) { + TODO("Not yet implemented") + } + + override fun logout() { storage.clear() } @@ -78,4 +102,11 @@ class MALRepository( return codeVerifier } + private fun MALUser(json: JSONObject) = ScrobblerUser( + id = json.getLong("id"), + nickname = json.getString("nickname"), + avatar = json.getString("avatar"), + service = ScrobblerService.SHIKIMORI, + ) + } diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/MALStorage.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/MALStorage.kt deleted file mode 100644 index a00af7ef0..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/MALStorage.kt +++ /dev/null @@ -1,41 +0,0 @@ -package org.koitharu.kotatsu.scrobbling.mal.data - -import android.content.Context -import androidx.core.content.edit -import dagger.hilt.android.qualifiers.ApplicationContext -import org.json.JSONObject -import org.koitharu.kotatsu.scrobbling.mal.data.model.MALUser -import javax.inject.Inject -import javax.inject.Singleton - -private const val PREF_NAME = "myanimelist" -private const val KEY_ACCESS_TOKEN = "access_token" -private const val KEY_REFRESH_TOKEN = "refresh_token" -private const val KEY_USER = "user" - -@Singleton -class MALStorage @Inject constructor(@ApplicationContext context: Context) { - - private val prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) - - var accessToken: String? - get() = prefs.getString(KEY_ACCESS_TOKEN, null) - set(value) = prefs.edit { putString(KEY_ACCESS_TOKEN, value) } - - var refreshToken: String? - get() = prefs.getString(KEY_REFRESH_TOKEN, null) - set(value) = prefs.edit { putString(KEY_REFRESH_TOKEN, value) } - - var user: MALUser? - get() = prefs.getString(KEY_USER, null)?.let { - MALUser(JSONObject(it)) - } - set(value) = prefs.edit { - putString(KEY_USER, value?.toJson()?.toString()) - } - - fun clear() = prefs.edit { - clear() - } - -} diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/model/MALUser.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/model/MALUser.kt deleted file mode 100644 index a8a4c9085..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/data/model/MALUser.kt +++ /dev/null @@ -1,37 +0,0 @@ -package org.koitharu.kotatsu.scrobbling.mal.data.model - -import org.json.JSONObject - -class MALUser( - val id: Long, - val nickname: String, -) { - - constructor(json: JSONObject) : this( - id = json.getLong("id"), - nickname = json.getString("name"), - ) - - fun toJson() = JSONObject().apply { - put("id", id) - put("nickname", nickname) - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as MALUser - - if (id != other.id) return false - if (nickname != other.nickname) return false - - return true - } - - override fun hashCode(): Int { - var result = id.hashCode() - result = 31 * result + nickname.hashCode() - return result - } -} diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/domain/MALScrobbler.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/domain/MALScrobbler.kt index 6789642f8..90a11a324 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/domain/MALScrobbler.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/domain/MALScrobbler.kt @@ -17,7 +17,7 @@ private const val RATING_MAX = 10f class MALScrobbler @Inject constructor( private val repository: MALRepository, db: MangaDatabase, -) : Scrobbler(db, ScrobblerService.MAL) { +) : Scrobbler(db, ScrobblerService.MAL, repository) { init { statuses[ScrobblingStatus.PLANNED] = "plan_to_read" @@ -27,21 +27,6 @@ class MALScrobbler @Inject constructor( statuses[ScrobblingStatus.DROPPED] = "dropped" } - override val isAvailable: Boolean - get() = repository.isAuthorized - - override suspend fun findManga(query: String, offset: Int): List { - TODO() - } - - override suspend fun linkManga(mangaId: Long, targetId: Long) { - TODO() - } - - override suspend fun scrobble(mangaId: Long, chapter: MangaChapter) { - TODO() - } - override suspend fun updateScrobblingInfo( mangaId: Long, rating: Float, @@ -51,11 +36,4 @@ class MALScrobbler @Inject constructor( TODO() } - override suspend fun unregisterScrobbling(mangaId: Long) { - repository.unregister(mangaId) - } - - override suspend fun getMangaInfo(id: Long): ScrobblerMangaInfo { - TODO() - } } diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/ui/MALSettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/ui/MALSettingsFragment.kt index 5903cdc2e..ce5377169 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/ui/MALSettingsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/ui/MALSettingsFragment.kt @@ -8,7 +8,7 @@ import androidx.preference.Preference import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BasePreferenceFragment -import org.koitharu.kotatsu.scrobbling.mal.data.model.MALUser +import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerUser import org.koitharu.kotatsu.utils.ext.assistedViewModels import org.koitharu.kotatsu.utils.ext.withArgs import javax.inject.Inject @@ -43,7 +43,7 @@ class MALSettingsFragment : BasePreferenceFragment(R.string.mal) { } } - private fun onUserChanged(user: MALUser?) { + private fun onUserChanged(user: ScrobblerUser?) { val pref = findPreference(KEY_USER) ?: return pref.isSelectable = user == null pref.title = user?.nickname ?: getString(R.string.sign_in) diff --git a/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/ui/MALSettingsViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/ui/MALSettingsViewModel.kt index 03ed32c5d..709837678 100644 --- a/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/ui/MALSettingsViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/scrobbling/mal/ui/MALSettingsViewModel.kt @@ -6,10 +6,8 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.Dispatchers import org.koitharu.kotatsu.base.ui.BaseViewModel +import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerUser import org.koitharu.kotatsu.scrobbling.mal.data.MALRepository -import org.koitharu.kotatsu.scrobbling.mal.data.model.MALUser -import org.koitharu.kotatsu.scrobbling.shikimori.data.ShikimoriRepository -import org.koitharu.kotatsu.scrobbling.shikimori.data.model.ShikimoriUser class MALSettingsViewModel @AssistedInject constructor( private val repository: MALRepository, @@ -19,7 +17,7 @@ class MALSettingsViewModel @AssistedInject constructor( val authorizationUrl: String get() = repository.oauthUrl - val user = MutableLiveData() + val user = MutableLiveData() init { if (authCode != null) { @@ -38,7 +36,7 @@ class MALSettingsViewModel @AssistedInject constructor( private fun loadUser() = launchJob(Dispatchers.Default) { val userModel = if (repository.isAuthorized) { - repository.getCachedUser()?.let(user::postValue) + repository.cachedUser?.let(user::postValue) repository.loadUser() } else { null diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/HistorySettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/settings/HistorySettingsFragment.kt index b43165eec..8fca72bbb 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/HistorySettingsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/HistorySettingsFragment.kt @@ -241,7 +241,7 @@ class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cach private fun bindMALSummary() { findPreference(AppSettings.KEY_MAL)?.summary = if (malRepository.isAuthorized) { - getString(R.string.logged_in_as, malRepository.getCachedUser()?.nickname) + getString(R.string.logged_in_as, malRepository.cachedUser?.nickname) } else { getString(R.string.disabled) } diff --git a/app/src/main/res/xml/pref_history.xml b/app/src/main/res/xml/pref_history.xml index 51a470fd1..2c3406f19 100644 --- a/app/src/main/res/xml/pref_history.xml +++ b/app/src/main/res/xml/pref_history.xml @@ -25,7 +25,6 @@ android:fragment="org.koitharu.kotatsu.scrobbling.shikimori.ui.ShikimoriSettingsFragment" android:icon="@drawable/ic_shikimori" android:key="shikimori" - android:icon="@drawable/ic_shikimori" android:title="@string/shikimori" />