add preferred content language selection

followup to 0b5a8e69fb
This commit is contained in:
Jannik 2022-03-04 20:29:37 +01:00
parent a51001ec2e
commit e8bf63a666
Signed by: Seil0
GPG Key ID: E8459F3723C52C24
5 changed files with 101 additions and 18 deletions

View File

@ -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'

View File

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

View File

@ -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<Profile> = lifecycleScope.async {
private var profile: Deferred<Profile> = 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),

View File

@ -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<ContinueWatchingItem>.toItemMediaList(): List<ItemMedia> {
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
}
}

View File

@ -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()