parent
a51001ec2e
commit
e8bf63a666
@ -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'
|
||||
|
@ -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())
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user