From e8bf63a6666e4e97bc9933997e0d9a919defddbc Mon Sep 17 00:00:00 2001 From: Jannik Date: Fri, 4 Mar 2022 20:29:37 +0100 Subject: [PATCH] add preferred content language selection followup to 0b5a8e69fb420383a2025aebdf911385ca174f67 --- app/build.gradle | 4 ++ .../teapod/parser/crunchyroll/Crunchyroll.kt | 58 ++++++++++++++++--- .../main/fragments/AccountFragment.kt | 44 ++++++++++---- .../main/java/org/mosad/teapod/util/Utils.kt | 12 +++- build.gradle | 1 + 5 files changed, 101 insertions(+), 18 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 7d7b6de..23d4d53 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -76,6 +76,10 @@ dependencies { implementation 'com.github.kittinunf.fuel:fuel-android:2.3.1' implementation 'com.github.kittinunf.fuel:fuel-json:2.3.1' + // TODO replace fuel with ktor + implementation "io.ktor:ktor-client-core:$ktor_version" + implementation "io.ktor:ktor-client-android:$ktor_version" + testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' diff --git a/app/src/main/java/org/mosad/teapod/parser/crunchyroll/Crunchyroll.kt b/app/src/main/java/org/mosad/teapod/parser/crunchyroll/Crunchyroll.kt index a77a103..18ff3e9 100644 --- a/app/src/main/java/org/mosad/teapod/parser/crunchyroll/Crunchyroll.kt +++ b/app/src/main/java/org/mosad/teapod/parser/crunchyroll/Crunchyroll.kt @@ -4,10 +4,13 @@ import android.util.Log import com.github.kittinunf.fuel.Fuel import com.github.kittinunf.fuel.core.FuelError import com.github.kittinunf.fuel.core.Parameters -import com.github.kittinunf.fuel.core.extensions.jsonBody import com.github.kittinunf.fuel.json.FuelJson import com.github.kittinunf.fuel.json.responseJson import com.github.kittinunf.result.Result +import io.ktor.client.* +import io.ktor.client.request.* +import io.ktor.client.statement.* +import io.ktor.http.* import kotlinx.coroutines.* import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json @@ -20,7 +23,9 @@ import org.mosad.teapod.util.concatenate private val json = Json { ignoreUnknownKeys = true } object Crunchyroll { + private val TAG = javaClass.name + private val client = HttpClient() private const val baseUrl = "https://beta-api.crunchyroll.com" private var accessToken = "" @@ -80,7 +85,7 @@ object Crunchyroll { // println("response: $response") // println("response: $result") - Log.i(javaClass.name, "login complete with code ${response.statusCode}") + Log.i(TAG, "login complete with code ${response.statusCode}") success = (response.statusCode == 200) } @@ -119,16 +124,46 @@ object Crunchyroll { private suspend fun requestPost( endpoint: String, params: Parameters = listOf(), - body: String + requestBody: String ) = coroutineScope { val path = "$baseUrl$endpoint" if (System.currentTimeMillis() > tokenValidUntil) refreshToken() withContext(Dispatchers.IO) { - Fuel.post(path, params) - .header("Authorization", "$tokenType $accessToken") - .jsonBody(body) - .response() // without a response, crunchy doesn't accept the request + val response: HttpResponse = client.request(path) { + method = HttpMethod.Post + body = requestBody + header("Authorization", "$tokenType $accessToken") + contentType(ContentType.Application.Json) + params.forEach { + parameter(it.first, it.second) + } + } + + Log.i(TAG, "Response status: ${response.status}") + } + } + + private suspend fun requestPatch( + endpoint: String, + params: Parameters = listOf(), + requestBody: String + ) = coroutineScope { + val path = "$baseUrl$endpoint" + if (System.currentTimeMillis() > tokenValidUntil) refreshToken() + + withContext(Dispatchers.IO) { + val response: HttpResponse = client.request(path) { + method = HttpMethod.Patch + body = requestBody + header("Authorization", "$tokenType $accessToken") + contentType(ContentType.Application.Json) + params.forEach { + parameter(it.first, it.second) + } + } + + Log.i(TAG, "Response status: ${response.status}") } } @@ -494,4 +529,13 @@ object Crunchyroll { } ?: NoneProfile } + suspend fun postPrefSubLanguage(languageTag: String) { + val profileEndpoint = "/accounts/v1/me/profile" + val json = buildJsonObject { + put("preferred_content_subtitle_language", languageTag) + } + + requestPatch(profileEndpoint, requestBody = json.toString()) + } + } diff --git a/app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/AccountFragment.kt b/app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/AccountFragment.kt index 55a8f74..68b86bb 100644 --- a/app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/AccountFragment.kt +++ b/app/src/main/java/org/mosad/teapod/ui/activity/main/fragments/AccountFragment.kt @@ -14,6 +14,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import kotlinx.coroutines.Deferred import kotlinx.coroutines.async import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import org.mosad.teapod.BuildConfig import org.mosad.teapod.R import org.mosad.teapod.databinding.FragmentAccountBinding @@ -26,12 +27,13 @@ import org.mosad.teapod.ui.activity.main.MainActivity import org.mosad.teapod.ui.components.LoginDialog import org.mosad.teapod.util.DataTypes.Theme import org.mosad.teapod.util.showFragment +import org.mosad.teapod.util.toDisplayString import java.util.* class AccountFragment : Fragment() { private lateinit var binding: FragmentAccountBinding - private val profile: Deferred = lifecycleScope.async { + private var profile: Deferred = lifecycleScope.async { Crunchyroll.profile() } @@ -107,6 +109,7 @@ class AccountFragment : Fragment() { //startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(AoDParser.getSubscriptionUrl()))) } + binding.linearSettingsContentLanguage.setOnClickListener { showContentLanguageSelection() } @@ -161,24 +164,45 @@ class AccountFragment : Fragment() { } private fun showContentLanguageSelection() { + // we should be able to use the index of supportedLocals for language selection, items is GUI only val items = supportedLocals.map { - if (it.displayLanguage.isNotEmpty() && it.displayCountry.isNotEmpty()) { - "${it.displayLanguage} (${it.displayCountry})" - } else if (it.displayCountry.isNotEmpty()) { - it.displayLanguage - } else { - getString(R.string.settings_content_language_none) - } + it.toDisplayString(getString(R.string.settings_content_language_none)) }.toTypedArray() + var initialSelection: Int + // profile should be completed here, therefore blocking + runBlocking { + initialSelection = supportedLocals.indexOf(Locale.forLanguageTag( + profile.await().preferredContentSubtitleLanguage)) + if (initialSelection < 0) initialSelection = supportedLocals.lastIndex + } + MaterialAlertDialogBuilder(requireContext()) .setTitle(R.string.settings_content_language) - .setSingleChoiceItems(items, 0){ _, which -> - // TODO + .setSingleChoiceItems(items, initialSelection){ dialog, which -> + updatePrefContentLanguage(supportedLocals[which].toLanguageTag()) + dialog.dismiss() } .show() } + @kotlinx.coroutines.ExperimentalCoroutinesApi + private fun updatePrefContentLanguage(languageTag: String) { + lifecycleScope.launch { + Crunchyroll.postPrefSubLanguage(languageTag) + + }.invokeOnCompletion { + // update profile since the language selection might have changed + profile = lifecycleScope.async { Crunchyroll.profile() } + profile.invokeOnCompletion { + // update language once loading profile is completed + binding.textSettingsContentLanguageDesc.text = Locale.forLanguageTag( + profile.getCompleted().preferredContentSubtitleLanguage + ).displayLanguage + } + } + } + private fun showThemeDialog() { val items = arrayOf( resources.getString(R.string.theme_light), diff --git a/app/src/main/java/org/mosad/teapod/util/Utils.kt b/app/src/main/java/org/mosad/teapod/util/Utils.kt index 28b6f5a..5118a00 100644 --- a/app/src/main/java/org/mosad/teapod/util/Utils.kt +++ b/app/src/main/java/org/mosad/teapod/util/Utils.kt @@ -3,8 +3,8 @@ package org.mosad.teapod.util import android.widget.TextView import org.mosad.teapod.parser.crunchyroll.Collection import org.mosad.teapod.parser.crunchyroll.ContinueWatchingItem -import org.mosad.teapod.parser.crunchyroll.ContinueWatchingList import org.mosad.teapod.parser.crunchyroll.Item +import java.util.* fun TextView.setDrawableTop(drawable: Int) { this.setCompoundDrawablesWithIntrinsicBounds(0, drawable, 0, 0) @@ -27,3 +27,13 @@ fun Collection.toItemMediaList(): List { ItemMedia(it.panel.episodeMetadata.seriesId, it.panel.title, it.panel.images.thumbnail[0][0].source) } } + +fun Locale.toDisplayString(fallback: String): String { + return if (this.displayLanguage.isNotEmpty() && this.displayCountry.isNotEmpty()) { + "${this.displayLanguage} (${this.displayCountry})" + } else if (this.displayCountry.isNotEmpty()) { + this.displayLanguage + } else { + fallback + } +} diff --git a/build.gradle b/build.gradle index f1d3eb9..f08ae57 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { ext.kotlin_version = "1.6.10" + ext.ktor_version = "1.6.7" repositories { google() mavenCentral()