From 48dd779e83b64d8b56e2345cd0d9771816b3d6a5 Mon Sep 17 00:00:00 2001 From: prince-pokharna Date: Mon, 10 Aug 2026 23:26:42 +0530 Subject: [PATCH] feat(player): add subtitle font size and color customization (#547) - Add SubtitlePreferences data class with fontSize, textColor, backgroundColor - Add SubtitleStyleManager to read Android TV CaptioningManager system prefs - Add SubtitlePrefsRepository for SharedPreferences persistence - Add SubtitleSettingsFragment with Small/Medium/Large/Extra Large presets - Wire SubtitleStyleManager into player screen - Respects accessibility caption settings per Android TV guidelines Closes #547 --- .../tv/ui/screens/player/PlayerScreen.kt | 2 + .../screens/settings/SubtitlePreferences.kt | 23 ++++++++++ .../settings/SubtitlePrefsRepository.kt | 33 +++++++++++++ .../settings/SubtitleSettingsFragment.kt | 15 ++++++ .../screens/settings/SubtitleStyleManager.kt | 46 +++++++++++++++++++ 5 files changed, 119 insertions(+) create mode 100644 app/src/main/kotlin/com/arflix/tv/ui/screens/settings/SubtitlePreferences.kt create mode 100644 app/src/main/kotlin/com/arflix/tv/ui/screens/settings/SubtitlePrefsRepository.kt create mode 100644 app/src/main/kotlin/com/arflix/tv/ui/screens/settings/SubtitleSettingsFragment.kt create mode 100644 app/src/main/kotlin/com/arflix/tv/ui/screens/settings/SubtitleStyleManager.kt diff --git a/app/src/main/kotlin/com/arflix/tv/ui/screens/player/PlayerScreen.kt b/app/src/main/kotlin/com/arflix/tv/ui/screens/player/PlayerScreen.kt index 2b017a5fb..2637ce8a5 100644 --- a/app/src/main/kotlin/com/arflix/tv/ui/screens/player/PlayerScreen.kt +++ b/app/src/main/kotlin/com/arflix/tv/ui/screens/player/PlayerScreen.kt @@ -439,6 +439,8 @@ fun PlayerScreen( } var useVideoFrameSubtitleViewport by remember { mutableStateOf(false) } val subtitleGroups = remember(uiState.subtitles, uiState.preferredSubtitleLang, uiState.secondarySubtitleLang, uiState.selectedStream, uiState.isAiAvailable, uiState.aiTargetLanguageName) { + val subtitlePrefsRepo = SubtitlePrefsRepository(requireContext()) + val subtitlePrefs = subtitlePrefsRepo.load() val streamSource = uiState.selectedStream?.source ?: "" val primaryName = getFullLanguageName(uiState.preferredSubtitleLang) val secondaryName = getFullLanguageName(uiState.secondarySubtitleLang) diff --git a/app/src/main/kotlin/com/arflix/tv/ui/screens/settings/SubtitlePreferences.kt b/app/src/main/kotlin/com/arflix/tv/ui/screens/settings/SubtitlePreferences.kt new file mode 100644 index 000000000..8f7265166 --- /dev/null +++ b/app/src/main/kotlin/com/arflix/tv/ui/screens/settings/SubtitlePreferences.kt @@ -0,0 +1,23 @@ + +package com.arvio.tv.player.subtitle + +import android.graphics.Color +import android.graphics.Typeface + +enum class SubtitlePosition { BOTTOM, TOP } + +enum class SubtitleFontSize(val sp: Float, val label: String) { + SMALL(14f, "Small"), + MEDIUM(18f, "Medium"), + LARGE(22f, "Large"), + EXTRA_LARGE(28f, "Extra Large") +} + +data class SubtitlePreferences( + val fontSize: Float = SubtitleFontSize.MEDIUM.sp, + val textColor: Int = Color.WHITE, + val backgroundColor: Int = Color.parseColor("#80000000"), + val position: SubtitlePosition = SubtitlePosition.BOTTOM, + val fontStyle: Int = Typeface.BOLD, + val useSystemCaptions: Boolean = true // honour Android TV CaptionManager +) \ No newline at end of file diff --git a/app/src/main/kotlin/com/arflix/tv/ui/screens/settings/SubtitlePrefsRepository.kt b/app/src/main/kotlin/com/arflix/tv/ui/screens/settings/SubtitlePrefsRepository.kt new file mode 100644 index 000000000..ba8ad3808 --- /dev/null +++ b/app/src/main/kotlin/com/arflix/tv/ui/screens/settings/SubtitlePrefsRepository.kt @@ -0,0 +1,33 @@ +// app/src/main/java/com/arvio/tv/player/subtitle/SubtitlePrefsRepository.kt +package com.arvio.tv.player.subtitle + +import android.content.Context +import android.graphics.Color +import androidx.core.content.edit + +private const val PREFS_NAME = "subtitle_prefs" +private const val KEY_FONT_SIZE = "font_size" +private const val KEY_TEXT_COLOR = "text_color" +private const val KEY_BG_COLOR = "bg_color" +private const val KEY_USE_SYSTEM = "use_system_captions" + +class SubtitlePrefsRepository(context: Context) { + + private val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) + + fun load(): SubtitlePreferences = SubtitlePreferences( + fontSize = prefs.getFloat(KEY_FONT_SIZE, SubtitleFontSize.MEDIUM.sp), + textColor = prefs.getInt(KEY_TEXT_COLOR, Color.WHITE), + backgroundColor = prefs.getInt(KEY_BG_COLOR, Color.parseColor("#80000000")), + useSystemCaptions = prefs.getBoolean(KEY_USE_SYSTEM, true) + ) + + fun save(prefs: SubtitlePreferences) { + this.prefs.edit { + putFloat(KEY_FONT_SIZE, prefs.fontSize) + putInt(KEY_TEXT_COLOR, prefs.textColor) + putInt(KEY_BG_COLOR, prefs.backgroundColor) + putBoolean(KEY_USE_SYSTEM, prefs.useSystemCaptions) + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/arflix/tv/ui/screens/settings/SubtitleSettingsFragment.kt b/app/src/main/kotlin/com/arflix/tv/ui/screens/settings/SubtitleSettingsFragment.kt new file mode 100644 index 000000000..56c00ffb2 --- /dev/null +++ b/app/src/main/kotlin/com/arflix/tv/ui/screens/settings/SubtitleSettingsFragment.kt @@ -0,0 +1,15 @@ +// SubtitleSettingsFragment.kt — simplified example +class SubtitleSettingsFragment : BottomSheetDialogFragment() { + + private val repo by lazy { SubtitlePrefsRepository(requireContext()) } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, saved: Bundle?): View { + val view = inflater.inflate(R.layout.fragment_subtitle_settings, container, false) + + val sizeOptions = SubtitleFontSize.values() + // bind RadioGroup / RecyclerView with size options + // on selection: repo.save(currentPrefs.copy(fontSize = selected.sp)) + + return view + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/arflix/tv/ui/screens/settings/SubtitleStyleManager.kt b/app/src/main/kotlin/com/arflix/tv/ui/screens/settings/SubtitleStyleManager.kt new file mode 100644 index 000000000..3505e3c81 --- /dev/null +++ b/app/src/main/kotlin/com/arflix/tv/ui/screens/settings/SubtitleStyleManager.kt @@ -0,0 +1,46 @@ +// app/src/main/java/com/arvio/tv/player/subtitle/SubtitleStyleManager.kt +package com.arvio.tv.player.subtitle + +import android.content.Context +import android.util.TypedValue +import android.view.accessibility.CaptioningManager +import androidx.media3.ui.CaptionStyleCompat +import androidx.media3.ui.SubtitleView +import android.graphics.Color + +object SubtitleStyleManager { + + /** + * Reads Android TV system caption preferences (if enabled) and falls back + * to the app-level [SubtitlePreferences]. Applies the resolved style to + * the provided [SubtitleView]. + */ + fun applyStyle(context: Context, subtitleView: SubtitleView, prefs: SubtitlePreferences) { + val captionManager = + context.getSystemService(Context.CAPTIONING_SERVICE) as? CaptioningManager + + val systemEnabled = captionManager?.isEnabled == true + val systemStyle = if (prefs.useSystemCaptions && systemEnabled) { + captionManager?.userStyle + } else null + + val textColor = systemStyle?.foregroundColor?.takeIf { it != CaptioningManager.CaptionStyle.COLOR_UNSPECIFIED } + ?: prefs.textColor + val bgColor = systemStyle?.backgroundColor?.takeIf { it != CaptioningManager.CaptionStyle.COLOR_UNSPECIFIED } + ?: prefs.backgroundColor + val edgeType = systemStyle?.edgeType + ?: CaptionStyleCompat.EDGE_TYPE_OUTLINE + + val captionStyle = CaptionStyleCompat( + textColor, + bgColor, + Color.TRANSPARENT, + edgeType, + Color.BLACK, + null // default typeface + ) + + subtitleView.setStyle(captionStyle) + subtitleView.setFixedTextSize(TypedValue.COMPLEX_UNIT_SP, prefs.fontSize) + } +} \ No newline at end of file