From 1a012cba7d7e226a8820495835053242623b9c4c Mon Sep 17 00:00:00 2001 From: Jannik Date: Fri, 21 Jul 2023 21:42:55 +0200 Subject: [PATCH] add support for dedicated subtitle and audio language settings --- app/build.gradle | 4 +- .../teapod/parser/crunchyroll/Crunchyroll.kt | 39 ++++----- .../teapod/parser/crunchyroll/DataTypes.kt | 30 ++++++- .../mosad/teapod/preferences/Preferences.kt | 14 ---- .../teapod/ui/activity/main/MainActivity.kt | 1 - .../main/fragments/AccountFragment.kt | 84 ++++++++++++++----- .../ui/activity/player/PlayerViewModel.kt | 9 +- .../drawable/ic_baseline_audiotrack_24.xml | 10 +++ app/src/main/res/layout/fragment_account.xml | 68 +++++---------- app/src/main/res/values-de-rDE/strings.xml | 3 +- app/src/main/res/values/strings.xml | 3 +- 11 files changed, 150 insertions(+), 115 deletions(-) create mode 100644 app/src/main/res/drawable/ic_baseline_audiotrack_24.xml diff --git a/app/build.gradle b/app/build.gradle index df29021..8db4cfa 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,8 +12,8 @@ android { applicationId "org.mosad.teapod" minSdkVersion 23 targetSdkVersion 33 - versionCode 100991 //01.00.000 - versionName "1.1.0-beta2" + versionCode 100992 //01.00.000 + versionName "1.1.0-beta3" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" resValue "string", "build_time", buildTime() 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 b7c1be5..2ddd151 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 @@ -64,10 +64,6 @@ object Crunchyroll { private var accountID = "" private var externalID = "" - private var policy = "" - private var signature = "" - private var keyPairID = "" - private val browsingCache = hashMapOf() /** @@ -212,27 +208,10 @@ object Crunchyroll { } /** - * Basic functions: index, account + * Basic functions: account * Needed for other functions to work properly! */ - /** - * Retrieve the identifiers necessary for streaming. If the identifiers are - * retrieved, set the corresponding global var. The identifiers are valid for 24h. - */ - suspend fun index() { - val indexEndpoint = "/index/v2" - - val index: Index = requestGet(indexEndpoint) - policy = index.cms.policy - signature = index.cms.signature - keyPairID = index.cms.keyPairId - - Log.i(TAG, "Policy : $policy") - Log.i(TAG, "Signature : $signature") - Log.i(TAG, "Key Pair ID : $keyPairID") - } - /** * Retrieve the account id and set the corresponding global var. * The account id is needed for other calls. @@ -757,7 +736,7 @@ object Crunchyroll { * * @param languageTag the preferred language as language tag */ - suspend fun postPrefSubLanguage(languageTag: String) { + suspend fun setPreferredSubtitleLanguage(languageTag: String) { val profileEndpoint = "/accounts/v1/me/profile" val json = buildJsonObject { put("preferred_content_subtitle_language", languageTag) @@ -766,6 +745,20 @@ object Crunchyroll { requestPatch(profileEndpoint, bodyObject = json) } + /** + * Patch the preferred content audio language. + * + * @param languageTag the preferred language as language tag + */ + suspend fun setPreferredAudioLanguage(languageTag: String) { + val profileEndpoint = "/accounts/v1/me/profile" + val json = buildJsonObject { + put("preferred_content_audio_language", languageTag) + } + + requestPatch(profileEndpoint, bodyObject = json) + } + /** * Get additional profile (benefits) information for the currently logged in account. * diff --git a/app/src/main/java/org/mosad/teapod/parser/crunchyroll/DataTypes.kt b/app/src/main/java/org/mosad/teapod/parser/crunchyroll/DataTypes.kt index 6cc85c4..2222e0c 100644 --- a/app/src/main/java/org/mosad/teapod/parser/crunchyroll/DataTypes.kt +++ b/app/src/main/java/org/mosad/teapod/parser/crunchyroll/DataTypes.kt @@ -26,17 +26,45 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import java.util.Locale -val supportedLocals = listOf( +val supportedAudioLocals = listOf( Locale.forLanguageTag("ar-SA"), + Locale.forLanguageTag("ca-ES"), + Locale.forLanguageTag("de-DE"), + Locale.forLanguageTag("en-US"), + Locale.forLanguageTag("en-IN"), + Locale.forLanguageTag("es-419"), + Locale.forLanguageTag("es-ES"), + Locale.forLanguageTag("fr-FR"), + Locale.forLanguageTag("hi-IN"), + Locale.forLanguageTag("it-IT"), + Locale.forLanguageTag("ko-KR"), + Locale.forLanguageTag("pl-PL"), + Locale.forLanguageTag("pt-BR"), + Locale.forLanguageTag("pt-PT"), + Locale.forLanguageTag("ru-RU"), + Locale.forLanguageTag("ta-IN"), + Locale.forLanguageTag("th-TH"), + Locale.forLanguageTag("zh-CN"), + Locale.forLanguageTag("zh-TW"), + Locale.ROOT +) + +val supportedSubtitleLocals = listOf( + Locale.forLanguageTag("ar-SA"), + Locale.forLanguageTag("ca-ES"), Locale.forLanguageTag("de-DE"), Locale.forLanguageTag("en-US"), Locale.forLanguageTag("es-419"), Locale.forLanguageTag("es-ES"), Locale.forLanguageTag("fr-FR"), + Locale.forLanguageTag("hi-IN"), Locale.forLanguageTag("it-IT"), + Locale.forLanguageTag("ms-MY"), + Locale.forLanguageTag("pl-PL"), Locale.forLanguageTag("pt-BR"), Locale.forLanguageTag("pt-PT"), Locale.forLanguageTag("ru-RU"), + Locale.forLanguageTag("tr-TR"), Locale.ROOT ) diff --git a/app/src/main/java/org/mosad/teapod/preferences/Preferences.kt b/app/src/main/java/org/mosad/teapod/preferences/Preferences.kt index 2bb3963..91f0f21 100644 --- a/app/src/main/java/org/mosad/teapod/preferences/Preferences.kt +++ b/app/src/main/java/org/mosad/teapod/preferences/Preferences.kt @@ -12,8 +12,6 @@ object Preferences { internal set var preferredSubtitleLocale: Locale = Locale.forLanguageTag("en-US") internal set - var preferSubbed = false - internal set var autoplay = true internal set var devSettings = false @@ -50,15 +48,6 @@ object Preferences { this.preferredSubtitleLocale = preferredLocale } - fun savePreferSecondary(context: Context, preferSubbed: Boolean) { - with(getSharedPref(context).edit()) { - putBoolean(context.getString(R.string.save_key_prefer_secondary), preferSubbed) - apply() - } - - this.preferSubbed = preferSubbed - } - fun saveAutoplay(context: Context, autoplay: Boolean) { with(getSharedPref(context).edit()) { putBoolean(context.getString(R.string.save_key_autoplay), autoplay) @@ -111,9 +100,6 @@ object Preferences { context.getString(R.string.save_key_preferred_local), "en-US" ) ?: "en-US" ) - preferSubbed = sharedPref.getBoolean( - context.getString(R.string.save_key_prefer_secondary), false - ) autoplay = sharedPref.getBoolean( context.getString(R.string.save_key_autoplay), true ) diff --git a/app/src/main/java/org/mosad/teapod/ui/activity/main/MainActivity.kt b/app/src/main/java/org/mosad/teapod/ui/activity/main/MainActivity.kt index 0144b60..1185e8c 100644 --- a/app/src/main/java/org/mosad/teapod/ui/activity/main/MainActivity.kt +++ b/app/src/main/java/org/mosad/teapod/ui/activity/main/MainActivity.kt @@ -173,7 +173,6 @@ class MainActivity : AppCompatActivity(), NavigationBarView.OnItemSelectedListen private fun initCrunchyroll(): List { val scope = CoroutineScope(Dispatchers.IO + CoroutineName("InitialCrunchyLoading")) return listOf( - scope.launch { Crunchyroll.index() }, scope.launch { Crunchyroll.account() }, scope.launch { // update the local preferred content language, since it may have changed 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 e04866a..f02e6c2 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 @@ -15,7 +15,8 @@ import org.mosad.teapod.databinding.FragmentAccountBinding import org.mosad.teapod.parser.crunchyroll.Benefits import org.mosad.teapod.parser.crunchyroll.Crunchyroll import org.mosad.teapod.parser.crunchyroll.Profile -import org.mosad.teapod.parser.crunchyroll.supportedLocals +import org.mosad.teapod.parser.crunchyroll.supportedAudioLocals +import org.mosad.teapod.parser.crunchyroll.supportedSubtitleLocals import org.mosad.teapod.preferences.EncryptedPreferences import org.mosad.teapod.preferences.Preferences import org.mosad.teapod.ui.activity.main.MainActivity @@ -61,11 +62,13 @@ class AccountFragment : Fragment() { // add preferred subtitles lifecycleScope.launch { - binding.textSettingsContentLanguageDesc.text = Locale.forLanguageTag( + binding.textSettingsAudioLanguageDesc.text = Locale.forLanguageTag( + profile.await().preferredContentAudioLanguage + ).displayLanguage + binding.textSettingsSubtitleLanguageDesc.text = Locale.forLanguageTag( profile.await().preferredContentSubtitleLanguage ).displayLanguage } - binding.switchSecondary.isChecked = Preferences.preferSubbed binding.switchAutoplay.isChecked = Preferences.autoplay binding.textThemeSelected.text = when (Preferences.theme) { Theme.SYSTEM -> getString(R.string.theme_system) @@ -86,12 +89,12 @@ class AccountFragment : Fragment() { showLoginDialog() } - binding.linearSettingsContentLanguage.setOnClickListener { - showContentLanguageSelection() + binding.linearSettingsAudioLanguage.setOnClickListener { + showAudioLanguageSelection() } - binding.switchSecondary.setOnClickListener { - Preferences.savePreferSecondary(requireContext(), binding.switchSecondary.isChecked) + binding.linearSettingsSubtitleLanguage.setOnClickListener { + showSubtitleLanguageSelection() } binding.switchAutoplay.setOnClickListener { @@ -136,43 +139,86 @@ class AccountFragment : Fragment() { activity?.let { loginModal.show(it.supportFragmentManager, LoginModalBottomSheet.TAG) } } - private fun showContentLanguageSelection() { + private fun showAudioLanguageSelection() { // we should be able to use the index of supportedLocals for language selection, items is GUI only - val items = supportedLocals.map { + val items = supportedAudioLocals.map { 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 + initialSelection = supportedAudioLocals.indexOf(Locale.forLanguageTag( + profile.await().preferredContentAudioLanguage)) + if (initialSelection < 0) initialSelection = supportedAudioLocals.lastIndex } MaterialAlertDialogBuilder(requireContext()) - .setTitle(R.string.settings_content_language) + .setTitle(R.string.settings_audio_language) .setSingleChoiceItems(items, initialSelection){ dialog, which -> - updatePrefContentLanguage(supportedLocals[which]) + updateAudioLanguage(supportedAudioLocals[which]) + dialog.dismiss() + } + .show() + } + + private fun showSubtitleLanguageSelection() { + // we should be able to use the index of supportedLocals for language selection, items is GUI only + val items = supportedSubtitleLocals.map { + it.toDisplayString(getString(R.string.settings_content_language_none)) + }.toTypedArray() + + var initialSelection: Int + // profile should be completed here, therefore blocking + runBlocking { + initialSelection = supportedSubtitleLocals.indexOf(Locale.forLanguageTag( + profile.await().preferredContentSubtitleLanguage)) + if (initialSelection < 0) initialSelection = supportedSubtitleLocals.lastIndex + } + + MaterialAlertDialogBuilder(requireContext()) + .setTitle(R.string.settings_audio_language) + .setSingleChoiceItems(items, initialSelection){ dialog, which -> + updateSubtitleLanguage(supportedSubtitleLocals[which]) dialog.dismiss() } .show() } @OptIn(ExperimentalCoroutinesApi::class) - private fun updatePrefContentLanguage(preferredLocale: Locale) { + private fun updateAudioLanguage(preferredLocale: Locale) { lifecycleScope.launch { - Crunchyroll.postPrefSubLanguage(preferredLocale.toLanguageTag()) + Crunchyroll.setPreferredAudioLanguage(preferredLocale.toLanguageTag()) }.invokeOnCompletion { - // update the local preferred content language - Preferences.savePreferredSubtitleLocal(requireContext(), preferredLocale) + // update the local preferred audio language + Preferences.savePreferredAudioLocal(requireContext(), preferredLocale) // 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( + binding.textSettingsAudioLanguageDesc.text = Locale.forLanguageTag( + profile.getCompleted().preferredContentAudioLanguage + ).displayLanguage + } + } + } + + @OptIn(ExperimentalCoroutinesApi::class) + private fun updateSubtitleLanguage(preferredLocal: Locale) { + lifecycleScope.launch { + Crunchyroll.setPreferredSubtitleLanguage(preferredLocal.toLanguageTag()) + + }.invokeOnCompletion { + // update the local preferred subtitle language + Preferences.savePreferredSubtitleLocal(requireContext(), preferredLocal) + + // update profile since the language selection might have changed + profile = lifecycleScope.async { Crunchyroll.profile() } + profile.invokeOnCompletion { + // update language once loading profile is completed + binding.textSettingsAudioLanguageDesc.text = Locale.forLanguageTag( profile.getCompleted().preferredContentSubtitleLanguage ).displayLanguage } diff --git a/app/src/main/java/org/mosad/teapod/ui/activity/player/PlayerViewModel.kt b/app/src/main/java/org/mosad/teapod/ui/activity/player/PlayerViewModel.kt index 24bafa0..6ade794 100644 --- a/app/src/main/java/org/mosad/teapod/ui/activity/player/PlayerViewModel.kt +++ b/app/src/main/java/org/mosad/teapod/ui/activity/player/PlayerViewModel.kt @@ -222,12 +222,9 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application) // needs to be blocking, currentPlayback must be present when calling playCurrentMedia() joinAll( viewModelScope.launch(Dispatchers.IO) { - currentVersion = if (Preferences.preferSubbed) { - currentEpisode.versions?.first { it.original } ?: NoneVersion - } else { - currentEpisode.versions?.firstOrNull { it.audioLocale == currentAudioLocale.toLanguageTag() } - ?: currentEpisode.versions?.first() ?: NoneVersion - } + currentVersion = currentEpisode.versions?.firstOrNull { + it.audioLocale == currentAudioLocale.toLanguageTag() + } ?: currentEpisode.versions?.first() ?: NoneVersion // get the current streams object, if no version is set, use streamsLink currentStreams = if (currentVersion != NoneVersion) { diff --git a/app/src/main/res/drawable/ic_baseline_audiotrack_24.xml b/app/src/main/res/drawable/ic_baseline_audiotrack_24.xml new file mode 100644 index 0000000..9ba72ee --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_audiotrack_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/fragment_account.xml b/app/src/main/res/layout/fragment_account.xml index 1422a5f..277a5bb 100644 --- a/app/src/main/res/layout/fragment_account.xml +++ b/app/src/main/res/layout/fragment_account.xml @@ -141,7 +141,7 @@ android:textStyle="bold" /> + android:src="@drawable/ic_baseline_audiotrack_24" /> @@ -179,67 +179,41 @@ - + android:layout_height="match_parent" + android:orientation="vertical"> - + android:text="@string/settings_subtitle_language" + android:textAppearance="@style/TextAppearance.Material3.BodyLarge" /> - - - - - - - - - + android:text="@string/settings_content_language_desc" /> + Info Version %1$s (%2$s) Einstellungen - Bevorzuge Inhaltssprache + Audio Sprache + Untertielsprache Englisch Keine Bevorzuge OmU diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a6896bb..c995ffb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -59,7 +59,8 @@ Mega Fan Ultimate Fan Settings - Preferred content language + Audio language + Subtitle language English None Prefer subbed