feat(ui): add custom theme color support and share app functionality

Introduce a new custom theme feature that allows users to personalize the application background color using RGB sliders, hex input, or a set of predefined palettes.

Key changes include:
* **Custom Theme Engine**: Added `CustomThemeFragment` and `ThemeHelper` to manage color selection and application. The UI dynamically updates the background color across the `MainActivity` and `PlayerFragment` when preferences change.
* **Android TV Support**: Provided a specialized layout for television devices (`fragment_custom_theme.xml` in `layout-television`) with optimized focus handling for D-pad navigation.
* **Share Feature**: Implemented a "Share App" preference in `SettingsFragment` that triggers a standard Android share intent and displays a thank-you notification upon use.
* **Localization**: Added Ukrainian language support and updated string resources for multiple locales (DE, DA, EL, FR, JA, NL, PL, RU) to include the new theme and share options.
* **Persistence**: Updated `PreferencesHelper` and `Keys` to store theme-related settings, including the enabled state, selected color, and predefined color index.
This commit is contained in:
2026-06-01 19:32:46 +02:00
parent 4429ed4057
commit 181ebd47df
29 changed files with 947 additions and 3 deletions
@@ -6,7 +6,6 @@ import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.drawable.AnimatedVectorDrawable
import java.util.Locale
import android.view.View
import android.view.animation.Animation
import android.view.animation.AnimationUtils
@@ -31,7 +30,9 @@ import com.michatec.radio.core.Station
import com.michatec.radio.helpers.DateTimeHelper
import com.michatec.radio.helpers.ImageHelper
import com.michatec.radio.helpers.PreferencesHelper
import com.michatec.radio.helpers.ThemeHelper
import com.michatec.radio.helpers.UiHelper
import java.util.Locale
/*
@@ -122,6 +123,9 @@ data class LayoutHolder(var rootView: View) {
CastButtonFactory.setUpMediaRouteButton(rootView.context, it)
}
// Apply custom theme color
applyCustomTheme(rootView.context)
// set layout for player
setupBottomSheet()
}
@@ -182,6 +186,9 @@ data class LayoutHolder(var rootView: View) {
// update bitrate
sheetBitrateView?.text = bitrateText
// update custom theme
applyCustomTheme(context)
// update click listeners
sheetStreamingLinkHeadline?.setOnClickListener {
copyToClipboard(
@@ -309,6 +316,28 @@ data class LayoutHolder(var rootView: View) {
isBuffering = buffering
}
/* Applies custom theme color to the UI */
fun applyCustomTheme(context: Context) {
val enabled = PreferencesHelper.loadCustomThemeEnabled()
if (enabled) {
var customColor = PreferencesHelper.loadCustomThemeColor(context)
val index = PreferencesHelper.loadCustomThemeIndex()
if (index != -1) {
val colors = ThemeHelper.getPredefinedColors(context)
if (index < colors.size) {
customColor = colors[index]
}
}
rootView.setBackgroundColor(customColor)
recyclerView.setBackgroundColor(customColor)
} else {
rootView.setBackgroundResource(android.R.color.transparent)
recyclerView.setBackgroundResource(android.R.color.transparent)
}
}
/* Toggles visibility of the download progress indicator */
fun toggleDownloadProgressIndicator() {
when (PreferencesHelper.loadActiveDownloads()) {