From 8514cc3da7efffe1d65b90a24f9ca488d6b4d417 Mon Sep 17 00:00:00 2001 From: Zakhar Timoshenko Date: Wed, 1 Feb 2023 01:05:04 +0300 Subject: [PATCH] Auth, search --- .../scrobbling/mal/data/MALRepository.kt | 121 +++++++++++++++--- .../scrobbling/mal/ui/MALSettingsFragment.kt | 13 ++ .../kotatsu/settings/SettingsActivity.kt | 5 + app/src/main/res/drawable-hdpi/ic_anilist.png | Bin 0 -> 804 bytes app/src/main/res/drawable-mdpi/ic_anilist.png | Bin 0 -> 517 bytes .../main/res/drawable-xhdpi/ic_anilist.png | Bin 0 -> 906 bytes .../main/res/drawable-xxhdpi/ic_anilist.png | Bin 0 -> 1745 bytes .../main/res/drawable-xxxhdpi/ic_anilist.png | Bin 0 -> 2149 bytes app/src/main/res/drawable/ic_anilist.xml | 10 -- app/src/main/res/xml/pref_history.xml | 3 +- 10 files changed, 124 insertions(+), 28 deletions(-) create mode 100644 app/src/main/res/drawable-hdpi/ic_anilist.png create mode 100644 app/src/main/res/drawable-mdpi/ic_anilist.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_anilist.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_anilist.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_anilist.png delete mode 100644 app/src/main/res/drawable/ic_anilist.xml 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 af423d496..d8d7be97b 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 @@ -1,15 +1,18 @@ package org.koitharu.kotatsu.scrobbling.mal.data import okhttp3.FormBody +import okhttp3.HttpUrl.Companion.toHttpUrl 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.json.mapJSON 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.data.ScrobblingEntity import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerManga import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerMangaInfo import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerService @@ -19,7 +22,8 @@ import org.koitharu.kotatsu.utils.PKCEGenerator private const val REDIRECT_URI = "kotatsu://mal-auth" private const val BASE_OAUTH_URL = "https://myanimelist.net" private const val BASE_API_URL = "https://api.myanimelist.net/v2" -private const val MANGA_PAGE_SIZE = 250 +private const val MANGA_PAGE_SIZE = 10 +private const val AVATAR_STUB = "https://cdn.myanimelist.net/images/questionmark_50.gif" // af16954886b040673378423f5d62cccd @@ -29,28 +33,32 @@ class MALRepository( private val db: MangaDatabase, ) : ScrobblerRepository { - private var codeVerifier: String = "" + private var codeVerifier: String = getPKCEChallengeCode() override val oauthUrl: String - get() = "${BASE_OAUTH_URL}/v1/oauth2/authorize?" + + get() = "$BASE_OAUTH_URL/v1/oauth2/authorize?" + "response_type=code" + "&client_id=af16954886b040673378423f5d62cccd" + - "&redirect_uri=${REDIRECT_URI}" + - "&code_challenge=${getPKCEChallengeCode()}" + + "&redirect_uri=$REDIRECT_URI" + + "&code_challenge=$codeVerifier" + "&code_challenge_method=plain" override val isAuthorized: Boolean get() = storage.accessToken != null + override val cachedUser: ScrobblerUser? - get() = TODO("Not yet implemented") + get() { + return storage.user + } override suspend fun authorize(code: String?) { val body = FormBody.Builder() if (code != null) { body.add("client_id", "af16954886b040673378423f5d62cccd") - body.add("code", code) - body.add("code_verifier", getPKCEChallengeCode()) body.add("grant_type", "authorization_code") + body.add("code", code) + body.add("redirect_uri", REDIRECT_URI) + body.add("code_verifier", codeVerifier) } val request = Request.Builder() .post(body.build()) @@ -64,7 +72,7 @@ class MALRepository( override suspend fun loadUser(): ScrobblerUser { val request = Request.Builder() .get() - .url("${BASE_API_URL}/users") + .url("${BASE_API_URL}/users/@me") val response = okHttp.newCall(request.build()).await().parseJson() return MALUser(response).also { storage.user = it } } @@ -74,23 +82,74 @@ class MALRepository( } override suspend fun findManga(query: String, offset: Int): List { - TODO("Not yet implemented") + val pageOffset = offset % MANGA_PAGE_SIZE + val url = BASE_API_URL.toHttpUrl().newBuilder() + .addPathSegment("manga") + .addQueryParameter("offset", (pageOffset + 1).toString()) + .addQueryParameter("nsfw", "true") + .addEncodedQueryParameter("q", query.take(64)) // WARNING! MAL API throws a 400 when the query is over 64 characters + .build() + val request = Request.Builder().url(url).get().build() + val response = okHttp.newCall(request).await().parseJson() + val data = response.getJSONArray("data") + val mangas = data.mapJSON { jsonToManga(it) } + return if (pageOffset != 0) mangas.drop(pageOffset) else mangas } override suspend fun getMangaInfo(id: Long): ScrobblerMangaInfo { - TODO("Not yet implemented") + val request = Request.Builder() + .get() + .url("${BASE_API_URL}/manga/$id") + val response = okHttp.newCall(request.build()).await().parseJson() + return ScrobblerMangaInfo(response) } override suspend fun createRate(mangaId: Long, scrobblerMangaId: Long) { - TODO("Not yet implemented") + val body = FormBody.Builder() + .add("status", "reading") + .add("score", "0") + val url = BASE_API_URL.toHttpUrl().newBuilder() + .addPathSegment("manga") + .addPathSegment(scrobblerMangaId.toString()) + .addPathSegment("my_list_status") + .build() + val request = Request.Builder() + .url(url) + .put(body.build()) + .build() + val response = okHttp.newCall(request).await().parseJson() } override suspend fun updateRate(rateId: Int, mangaId: Long, chapter: MangaChapter) { - TODO("Not yet implemented") + val body = FormBody.Builder() + .add("status", "reading") + .add("score", "0") + val url = BASE_API_URL.toHttpUrl().newBuilder() + .addPathSegment("manga") + .addPathSegment(mangaId.toString()) + .addPathSegment("my_list_status") + .build() + val request = Request.Builder() + .url(url) + .put(body.build()) + .build() + val response = okHttp.newCall(request).await().parseJson() } override suspend fun updateRate(rateId: Int, mangaId: Long, rating: Float, status: String?, comment: String?) { - TODO("Not yet implemented") + val body = FormBody.Builder() + .add("status", status!!) + .add("score", rating.toString()) + val url = BASE_API_URL.toHttpUrl().newBuilder() + .addPathSegment("manga") + .addPathSegment(mangaId.toString()) + .addPathSegment("my_list_status") + .build() + val request = Request.Builder() + .url(url) + .put(body.build()) + .build() + val response = okHttp.newCall(request).await().parseJson() } override fun logout() { @@ -102,11 +161,39 @@ class MALRepository( return codeVerifier } + private fun jsonToManga(json: JSONObject): ScrobblerManga { + for (i in 0 until json.length()) { + val node = json.getJSONObject("node") + return ScrobblerManga( + id = node.getLong("id"), + name = node.getString("title"), + altName = null, + cover = node.getJSONObject("main_picture").getString("large"), + url = "" + ) + } + return ScrobblerManga( + id = 1, + name = "", + altName = null, + cover = "", + url = "" + ) + } + + private fun ScrobblerMangaInfo(json: JSONObject) = ScrobblerMangaInfo( + id = json.getLong("id"), + name = json.getString("title"), + cover = json.getJSONObject("main_picture").getString("large"), + url = "", + descriptionHtml = json.getString("synopsis"), + ) + private fun MALUser(json: JSONObject) = ScrobblerUser( id = json.getLong("id"), - nickname = json.getString("nickname"), - avatar = json.getString("avatar"), - service = ScrobblerService.SHIKIMORI, + nickname = json.getString("name"), + avatar = json.getString("picture") ?: AVATAR_STUB, + service = ScrobblerService.MAL, ) } 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 ce5377169..ec0b185cd 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 @@ -5,17 +5,25 @@ import android.net.Uri import android.os.Bundle import android.view.View import androidx.preference.Preference +import coil.ImageLoader +import coil.request.ImageRequest +import coil.transform.CircleCropTransformation import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BasePreferenceFragment import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblerUser +import org.koitharu.kotatsu.utils.PreferenceIconTarget import org.koitharu.kotatsu.utils.ext.assistedViewModels +import org.koitharu.kotatsu.utils.ext.enqueueWith import org.koitharu.kotatsu.utils.ext.withArgs import javax.inject.Inject @AndroidEntryPoint class MALSettingsFragment : BasePreferenceFragment(R.string.mal) { + @Inject + lateinit var coil: ImageLoader + @Inject lateinit var viewModelFactory: MALSettingsViewModel.Factory @@ -47,6 +55,11 @@ class MALSettingsFragment : BasePreferenceFragment(R.string.mal) { val pref = findPreference(KEY_USER) ?: return pref.isSelectable = user == null pref.title = user?.nickname ?: getString(R.string.sign_in) + ImageRequest.Builder(requireContext()) + .data(user?.avatar) + .transformations(CircleCropTransformation()) + .target(PreferenceIconTarget(pref)) + .enqueueWith(coil) findPreference(KEY_LOGOUT)?.isVisible = user != null } diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/SettingsActivity.kt b/app/src/main/java/org/koitharu/kotatsu/settings/SettingsActivity.kt index e6f8880aa..21bea36ed 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/SettingsActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/SettingsActivity.kt @@ -25,6 +25,7 @@ import org.koitharu.kotatsu.databinding.ActivitySettingsBinding import org.koitharu.kotatsu.main.ui.owners.AppBarOwner import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.scrobbling.anilist.ui.AniListSettingsFragment +import org.koitharu.kotatsu.scrobbling.mal.ui.MALSettingsFragment import org.koitharu.kotatsu.scrobbling.shikimori.ui.ShikimoriSettingsFragment import org.koitharu.kotatsu.settings.sources.SourcesSettingsFragment import org.koitharu.kotatsu.settings.tracker.TrackerSettingsFragment @@ -151,6 +152,9 @@ class SettingsActivity : HOST_ANILIST_AUTH -> return AniListSettingsFragment.newInstance(authCode = uri.getQueryParameter("code")) + + HOST_MAL_AUTH -> + return MALSettingsFragment.newInstance(authCode = uri.getQueryParameter("code")) } finishAfterTransition() return null @@ -169,6 +173,7 @@ class SettingsActivity : private const val HOST_SHIKIMORI_AUTH = "shikimori-auth" private const val HOST_ANILIST_AUTH = "anilist-auth" + private const val HOST_MAL_AUTH = "mal-auth" fun newIntent(context: Context) = Intent(context, SettingsActivity::class.java) diff --git a/app/src/main/res/drawable-hdpi/ic_anilist.png b/app/src/main/res/drawable-hdpi/ic_anilist.png new file mode 100644 index 0000000000000000000000000000000000000000..fdb33acef062469101a04008d7441770dbfc3c5a GIT binary patch literal 804 zcmV+<1Ka$GP)Px%-bqA3RA@u(*h{FDRTu`~=Xk-)yn;;vwFV6YHFe@ZL`y{!L@GnnQBHIqPQzQ? zKoA6-SQ)7x4n$GZQIezuPQ-x_A&Q9;K_?cqE0-um%Eu;vwE^)2o#b_yETde;e1&DYT+F zwNo6rP9Q+7!%>VbAg~$taSU&_p+eUQM9d$;h5~SZ<6VqO3N09n1fuF(O+Xv*{fOud z(clhZQ^W99#HZ^DtWCpPWAX;L0Q_}A8MXa8jBlYr*9lz4k^)dqF&FpJZy%W-1m@$1G@vya z3$M?{uPso_Ng+71Q?w2H`xQc8VPlEmcn^yAo?7!N!#5V+r1zgz`PPi%-Le;uW|w}^ zBmuuiREBoeM~Q6x(_*W0;h36pqKrn*h=S2^6>4Ru^51i0tsHy+=n<$|hModF1u{L* i3!zL0W*;B&3j7aOCvP!pwkym40000Px$zez+vR9Hvt*1c;MK@i9BPkalApd#9cim0tvSSV;?V^V|^Hj-Yj5f%Rr->}hM zY%DAUZERvG2$Cuo1WiC8pp{m>F>;S=mOb5*l{C**cYC|P`QFUz%uZP6F|p3}jRD3U z7~+8)*peje53C~QH}fl>zT)GM#2y0h3P&@516azyX7IQcq84Be-eW^1`~+vS`AfLd z8sHu-6htEo`A=NN?bZPAu&+|IIlz9r!Ny8}tGHRYwyF|7#JNQ9dm$H6drbl2*nPs* z#KH)uFM*o^9Km9iXeV$3Cwtu76d;b-RAOkQOU60e|K9*R3svnFuHayom?eg09ypEp z%)&IDb|HC*V+Dd{0MBqHGZ3{tPC<-KJ*S}wz+ObW|7Bdm_1cYnN9jBs)QV*5B5W_Z zxskWxHJ%spb;)(9LrlpqvB7Vm+mAH6BN6bg;e>Px&L`g(JRCr$PnM=q{Q51%sA4y5>#6$*)kqDtk48+8xC>hAW#AToq0|rb;DNzP8 zFu=&IPYXNWvxCl(k@ZIBi z2doFqg;HwW1oQ>o0K+W;J_1|;jvKJT%(w981*w~W5x{FeM40Rcum{*_0Nw&-nVk8) zubY6~z&3|qt^;!n_}jn?2cJ&RItds6e6v{lP5}ZPL%>Affopaa_>LjqAh6LQAy-I8 z5HJW($?ET#odsT{a=Hc13sTn}FfS`n{Uz!_j^NoN0mEx|u>f30wn+EK);e?erNK3TfnFn_~_UZM5zFq zlb`PT7PlbCMmfb{Q4;VJ7-x{O*8XWgjZo2a19aYD8AM0GFhH_4-TEr={K@mp4_~IS zQ7hmWu-ah;t3snvfKGfLfPq!Bau|OfnCyUeg1X)ZOaLBv_ViAH6~Ivua28nNn~CSR z$G}S9Phq_FmcMQM38(j8fLEsfbWy-y;FHN=WbZcv`^#c-AwVsxTG>vr&%ltn2)F<& zvdI2xi`w66m`Y(!Y^J4?Y^#7Vzzd7bPmGW1wqDzDCIaMhqy?!}Ahi&%95`hW#a3Xi z1ulp5L{Cu*0X7#O`r4~5Q5T@Lwk&IxZSg+1$^?uDo><8LAu!1?LY)q^3`ATAQ%Nj4 zv08l?vAOqC@UHUD6Fv;A3z2>Hxvq4NvDpN#0W%E&O8&YdaFKuRQb%SrSXHchW27eO zJD^)D9o^fS4FOdGi+*e(Ao?i^9AHvFV1lD}pA-=N6a@}2DIhSx(YsFyh<=I!2bdHP gnBeH$H(mk%0acfSK|qr0vj6}907*qoM6N<$f)?G9kN^Mx literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/ic_anilist.png b/app/src/main/res/drawable-xxhdpi/ic_anilist.png new file mode 100644 index 0000000000000000000000000000000000000000..fb392692c309c10e3dec5c8836f90787d39bf536 GIT binary patch literal 1745 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 F{|`JR@$3Kq literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/ic_anilist.xml b/app/src/main/res/drawable/ic_anilist.xml deleted file mode 100644 index e9fa65813..000000000 --- a/app/src/main/res/drawable/ic_anilist.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/xml/pref_history.xml b/app/src/main/res/xml/pref_history.xml index 2c3406f19..b0975f4c5 100644 --- a/app/src/main/res/xml/pref_history.xml +++ b/app/src/main/res/xml/pref_history.xml @@ -1,6 +1,7 @@ + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto">