mirror of
https://github.com/Michatec/Radio.git
synced 2026-06-03 17:41:14 +02:00
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:
@@ -0,0 +1,207 @@
|
||||
package com.michatec.radio
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.SeekBar
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.graphics.toColorInt
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import com.michatec.radio.helpers.PreferencesHelper
|
||||
import com.michatec.radio.helpers.ThemeHelper
|
||||
|
||||
class CustomThemeFragment : Fragment() {
|
||||
|
||||
private lateinit var colorPreview: View
|
||||
private lateinit var hexCode: TextInputEditText
|
||||
private lateinit var seekRed: SeekBar
|
||||
private lateinit var seekGreen: SeekBar
|
||||
private lateinit var seekBlue: SeekBar
|
||||
private lateinit var recyclerView: RecyclerView
|
||||
|
||||
private var currentColor: Int = Color.BLACK
|
||||
private var isUpdatingFromHex = false
|
||||
|
||||
private fun applyColor(
|
||||
color: Int
|
||||
) {
|
||||
updateSeekBars(color)
|
||||
updatePreview(color)
|
||||
}
|
||||
|
||||
private val isAndroidTV: Boolean by lazy {
|
||||
requireContext().packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_custom_theme, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
(activity as? AppCompatActivity)?.supportActionBar?.title = getString(R.string.pref_custom_theme_title)
|
||||
|
||||
colorPreview = view.findViewById(R.id.color_preview)
|
||||
hexCode = view.findViewById(R.id.hex_code)
|
||||
seekRed = view.findViewById(R.id.seek_red)
|
||||
seekGreen = view.findViewById(R.id.seek_green)
|
||||
seekBlue = view.findViewById(R.id.seek_blue)
|
||||
recyclerView = view.findViewById(R.id.color_recycler_view)
|
||||
|
||||
currentColor = PreferencesHelper.loadCustomThemeColor(requireContext())
|
||||
|
||||
applyColor(currentColor)
|
||||
|
||||
val seekBarListener = object : SeekBar.OnSeekBarChangeListener {
|
||||
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
val r = seekRed.progress
|
||||
val g = seekGreen.progress
|
||||
val b = seekBlue.progress
|
||||
currentColor = Color.rgb(r, g, b)
|
||||
updatePreview(currentColor)
|
||||
PreferencesHelper.saveCustomTheme(currentColor, -1)
|
||||
(recyclerView.adapter as? ColorAdapter)?.resetSelection()
|
||||
}
|
||||
}
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar?) {}
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar?) {}
|
||||
}
|
||||
|
||||
seekRed.setOnSeekBarChangeListener(seekBarListener)
|
||||
seekGreen.setOnSeekBarChangeListener(seekBarListener)
|
||||
seekBlue.setOnSeekBarChangeListener(seekBarListener)
|
||||
|
||||
// Clipboard logic (Non-TV)
|
||||
if (!isAndroidTV) {
|
||||
hexCode.setOnClickListener {
|
||||
copyToClipboard(hexCode.text.toString())
|
||||
}
|
||||
hexCode.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
||||
if (!isUpdatingFromHex) {
|
||||
try {
|
||||
val color = s.toString().toColorInt()
|
||||
currentColor = color
|
||||
isUpdatingFromHex = true
|
||||
applyColor(color)
|
||||
PreferencesHelper.saveCustomTheme(currentColor, -1)
|
||||
(recyclerView.adapter as? ColorAdapter)?.resetSelection()
|
||||
isUpdatingFromHex = false
|
||||
} catch (_: Exception) {}
|
||||
}
|
||||
}
|
||||
override fun afterTextChanged(s: Editable?) {}
|
||||
})
|
||||
} else {
|
||||
hexCode.isFocusable = false
|
||||
hexCode.isFocusableInTouchMode = false
|
||||
}
|
||||
|
||||
setupRecyclerView()
|
||||
}
|
||||
|
||||
private fun copyToClipboard(text: String) {
|
||||
val clipboard = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clip = ClipData.newPlainText(getString(R.string.hex_code), text)
|
||||
clipboard.setPrimaryClip(clip)
|
||||
Toast.makeText(requireContext(), R.string.toastmessage_copied_to_clipboard, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
private fun updateSeekBars(color: Int) {
|
||||
seekRed.progress = Color.red(color)
|
||||
seekGreen.progress = Color.green(color)
|
||||
seekBlue.progress = Color.blue(color)
|
||||
}
|
||||
|
||||
private fun updatePreview(color: Int) {
|
||||
colorPreview.setBackgroundColor(color)
|
||||
if (!isUpdatingFromHex) {
|
||||
isUpdatingFromHex = true
|
||||
hexCode.setText(String.format("#%08X", 0xFFFFFF and color))
|
||||
isUpdatingFromHex = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
recyclerView.layoutManager = GridLayoutManager(requireContext(), 5)
|
||||
val colors = ThemeHelper.getPredefinedColors(requireContext())
|
||||
val adapter = ColorAdapter(colors) { color, index ->
|
||||
currentColor = color
|
||||
applyColor(color)
|
||||
PreferencesHelper.saveCustomTheme(currentColor, index)
|
||||
}
|
||||
recyclerView.adapter = adapter
|
||||
}
|
||||
|
||||
private inner class ColorAdapter(
|
||||
private val colors: List<Int>,
|
||||
private val onColorSelected: (Int, Int) -> Unit
|
||||
) : RecyclerView.Adapter<ColorAdapter.ViewHolder>() {
|
||||
|
||||
private var selectedPosition: Int = -1
|
||||
|
||||
init {
|
||||
selectedPosition = PreferencesHelper.loadCustomThemeIndex()
|
||||
}
|
||||
|
||||
fun resetSelection() {
|
||||
val oldPos = selectedPosition
|
||||
selectedPosition = -1
|
||||
if (oldPos != -1) notifyItemChanged(oldPos)
|
||||
}
|
||||
|
||||
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
val circle: View = view.findViewById(R.id.color_circle)
|
||||
init {
|
||||
view.isFocusable = true
|
||||
view.isFocusableInTouchMode = isAndroidTV
|
||||
view.setOnClickListener {
|
||||
val pos = bindingAdapterPosition
|
||||
if (pos != RecyclerView.NO_POSITION) {
|
||||
val oldPos = selectedPosition
|
||||
selectedPosition = pos
|
||||
if (oldPos != -1) notifyItemChanged(oldPos)
|
||||
notifyItemChanged(selectedPosition)
|
||||
onColorSelected(colors[pos], pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.element_color_circle, parent, false)
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val color = colors[position]
|
||||
val drawable = holder.circle.background as GradientDrawable
|
||||
drawable.setColor(color)
|
||||
|
||||
// Set selection state
|
||||
holder.itemView.isSelected = (position == selectedPosition)
|
||||
}
|
||||
|
||||
override fun getItemCount() = colors.size
|
||||
}
|
||||
}
|
||||
@@ -92,6 +92,9 @@ object Keys {
|
||||
const val PREF_PRESET_DRC: String = "PRESET_DRC"
|
||||
const val PREF_PRESET_STEREO_WIDTH: String = "PRESET_STEREO_WIDTH"
|
||||
const val PREF_LANGUAGE_SELECTED: String = "PRESET_LANGUAGE_SELECTED"
|
||||
const val PREF_CUSTOM_THEME_COLOR: String = "CUSTOM_THEME_COLOR"
|
||||
const val PREF_CUSTOM_THEME_ENABLED: String = "CUSTOM_THEME_ENABLED"
|
||||
const val PREF_CUSTOM_THEME_INDEX: String = "CUSTOM_THEME_INDEX"
|
||||
|
||||
// default const values
|
||||
const val DEFAULT_SIZE_OF_METADATA_HISTORY: Int = 25
|
||||
|
||||
@@ -7,6 +7,7 @@ import android.content.SharedPreferences
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Configuration
|
||||
import android.net.Uri
|
||||
import android.util.TypedValue
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
@@ -28,6 +29,7 @@ import com.michatec.radio.helpers.AppThemeHelper
|
||||
import com.michatec.radio.helpers.FileHelper
|
||||
import com.michatec.radio.helpers.LanguageHelper
|
||||
import com.michatec.radio.helpers.PreferencesHelper
|
||||
import com.michatec.radio.helpers.ThemeHelper
|
||||
import org.woheller69.freeDroidWarn.FreeDroidWarn
|
||||
import java.util.Locale
|
||||
|
||||
@@ -38,6 +40,7 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
/* Main class variables */
|
||||
private lateinit var appBarConfiguration: AppBarConfiguration
|
||||
private lateinit var mainRoot: View
|
||||
|
||||
// Check if the device running the app is an Android TV instance
|
||||
private val isAndroidTV: Boolean by lazy {
|
||||
@@ -94,6 +97,8 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
// set up views
|
||||
setContentView(R.layout.activity_main)
|
||||
mainRoot = findViewById(R.id.main_root)
|
||||
applyCustomTheme()
|
||||
|
||||
// create .nomedia file - if not yet existing
|
||||
FileHelper.createNomediaFile(getExternalFilesDir(null))
|
||||
@@ -136,6 +141,33 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyCustomTheme() {
|
||||
val enabled = PreferencesHelper.loadCustomThemeEnabled()
|
||||
if (enabled) {
|
||||
var color = PreferencesHelper.loadCustomThemeColor(this)
|
||||
val index = PreferencesHelper.loadCustomThemeIndex()
|
||||
|
||||
if (index != -1) {
|
||||
// Color belongs to a predefined group. Update it based on current mode.
|
||||
val colors = ThemeHelper.getPredefinedColors(this)
|
||||
if (index < colors.size) {
|
||||
val updatedColor = colors[index]
|
||||
if (updatedColor != color) {
|
||||
color = updatedColor
|
||||
// Save the updated color to keep preferences in sync with the current mode
|
||||
PreferencesHelper.saveCustomThemeColor(color)
|
||||
}
|
||||
}
|
||||
}
|
||||
mainRoot.setBackgroundColor(color)
|
||||
} else {
|
||||
// Reset to default theme background color
|
||||
val typedValue = TypedValue()
|
||||
theme.resolveAttribute(android.R.attr.colorBackground, typedValue, true)
|
||||
mainRoot.setBackgroundColor(typedValue.data)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Overrides onResume from AppCompatActivity */
|
||||
override fun onResume() {
|
||||
@@ -176,6 +208,9 @@ class MainActivity : AppCompatActivity() {
|
||||
Keys.PREF_LANGUAGE_SELECTED -> {
|
||||
LanguageHelper.setLanguage(this, PreferencesHelper.loadSelectedLanguage())
|
||||
}
|
||||
Keys.PREF_CUSTOM_THEME_COLOR, Keys.PREF_CUSTOM_THEME_ENABLED, Keys.PREF_CUSTOM_THEME_INDEX -> {
|
||||
applyCustomTheme()
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
|
||||
@@ -308,6 +308,9 @@ class PlayerFragment : Fragment(),
|
||||
if (key == Keys.PREF_PLAYER_METADATA_HISTORY) {
|
||||
requestMetadataUpdate()
|
||||
}
|
||||
if (key == Keys.PREF_CUSTOM_THEME_COLOR || key == Keys.PREF_CUSTOM_THEME_ENABLED || key == Keys.PREF_CUSTOM_THEME_INDEX) {
|
||||
layout.applyCustomTheme(activity as Context)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -265,6 +265,7 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
|
||||
// set up "Visualizer" preference entry
|
||||
val preferenceVisualizer = Preference(context)
|
||||
preferenceVisualizer.title = getString(R.string.pref_visualizer_title)
|
||||
preferenceVisualizer.setIcon(R.drawable.ic_visualizer_24dp)
|
||||
@@ -349,6 +350,7 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
|
||||
// set up "Language Selection" preference
|
||||
val preferenceLanguageSelection = Preference(context)
|
||||
preferenceLanguageSelection.title = getString(R.string.pref_language_selection_title)
|
||||
preferenceLanguageSelection.setIcon(R.drawable.ic_language_24dp)
|
||||
@@ -361,6 +363,61 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
|
||||
// set up "Custom Theme" preference
|
||||
val preferenceCustomTheme = Preference(context)
|
||||
preferenceCustomTheme.title = getString(R.string.pref_custom_theme_title)
|
||||
preferenceCustomTheme.setIcon(R.drawable.ic_rbrush_24dp)
|
||||
preferenceCustomTheme.summary = getString(R.string.pref_custom_theme_summary)
|
||||
preferenceCustomTheme.isEnabled = PreferencesHelper.loadCustomThemeEnabled()
|
||||
preferenceCustomTheme.setOnPreferenceClickListener {
|
||||
findNavController().navigate(R.id.action_settings_to_cstheme)
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
|
||||
// set up "Custom Theme Enabled" preference
|
||||
val preferenceCustomThemeEnabled = MarqueeSwitchPreference(context)
|
||||
preferenceCustomThemeEnabled.title = getString(R.string.pref_custom_theme_enabled_title)
|
||||
preferenceCustomThemeEnabled.setIcon(R.drawable.ic_rbrush_24dp)
|
||||
preferenceCustomThemeEnabled.summaryOn = getString(R.string.pref_custom_theme_enabled_summary)
|
||||
preferenceCustomThemeEnabled.summaryOff = getString(R.string.pref_custom_theme_disabled_summary)
|
||||
preferenceCustomThemeEnabled.key = Keys.PREF_CUSTOM_THEME_ENABLED
|
||||
preferenceCustomThemeEnabled.setDefaultValue(PreferencesHelper.loadCustomThemeEnabled())
|
||||
preferenceCustomThemeEnabled.setOnPreferenceChangeListener { _, newValue ->
|
||||
when (newValue) {
|
||||
true -> {
|
||||
// enable custom theme
|
||||
preferenceCustomTheme.isEnabled = true
|
||||
}
|
||||
false -> {
|
||||
// disable custom theme
|
||||
preferenceCustomTheme.isEnabled = false
|
||||
}
|
||||
}
|
||||
return@setOnPreferenceChangeListener true
|
||||
}
|
||||
|
||||
// set up "Share the App" preference
|
||||
val preferenceShareApp = Preference(context)
|
||||
preferenceShareApp.title = getString(R.string.pref_share_app_title)
|
||||
preferenceShareApp.setIcon(R.drawable.ic_share_24dp)
|
||||
preferenceShareApp.summary = getString(R.string.pref_share_app_summary)
|
||||
preferenceShareApp.setOnPreferenceClickListener {
|
||||
val shareIntent = Intent().apply {
|
||||
action = Intent.ACTION_SEND
|
||||
type = "text/plain"
|
||||
putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.app_name))
|
||||
putExtra(Intent.EXTRA_TEXT, getString(R.string.pref_share_app_share_text))
|
||||
}
|
||||
startActivity(shareIntent)
|
||||
if (!isAndroidTV && isPermissionGranted(activity as Context, android.Manifest.permission.POST_NOTIFICATIONS)) {
|
||||
NotificationSys.showNotification(
|
||||
context,
|
||||
getString(R.string.pref_share_app_thank_title),
|
||||
getString(R.string.pref_share_app_thank_message)
|
||||
)
|
||||
}
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
|
||||
// set preference categories
|
||||
val preferenceCategoryGeneral = PreferenceCategory(activity as Context)
|
||||
@@ -384,10 +441,13 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
||||
|
||||
// setup preference screen
|
||||
screen.addPreference(preferenceAppVersion)
|
||||
screen.addPreference(preferenceShareApp)
|
||||
|
||||
screen.addPreference(preferenceCategoryGeneral)
|
||||
preferenceCategoryGeneral.addPreference(preferenceThemeSelection)
|
||||
preferenceCategoryGeneral.addPreference(preferenceLanguageSelection)
|
||||
preferenceCategoryGeneral.addPreference(preferenceCustomThemeEnabled)
|
||||
preferenceCategoryGeneral.addPreference(preferenceCustomTheme)
|
||||
|
||||
if (!isAndroidTV && isPermissionGranted(activity as Context, android.Manifest.permission.POST_NOTIFICATIONS)) {
|
||||
preferenceCategoryGeneral.addPreference(preferenceTestNotification)
|
||||
|
||||
@@ -39,6 +39,7 @@ class LanguageSelectionDialog(private var languageSelectionDialogListener: Langu
|
||||
Language("de", R.string.pref_language_de),
|
||||
Language("fr", R.string.pref_language_fr),
|
||||
Language("ru", R.string.pref_language_ru),
|
||||
Language("uk", R.string.pref_language_uk),
|
||||
Language("ja", R.string.pref_language_ja),
|
||||
Language("nl", R.string.pref_language_nl),
|
||||
Language("pl", R.string.pref_language_pl),
|
||||
|
||||
@@ -4,12 +4,14 @@ import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.content.pm.PackageManager
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
import androidx.core.content.edit
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.google.gson.Gson
|
||||
import com.michatec.radio.Keys
|
||||
import com.michatec.radio.ui.PlayerState
|
||||
import java.util.*
|
||||
import java.util.Calendar
|
||||
import java.util.Date
|
||||
|
||||
|
||||
/*
|
||||
@@ -362,4 +364,33 @@ object PreferencesHelper {
|
||||
}
|
||||
}
|
||||
|
||||
/* Loads custom theme color */
|
||||
fun loadCustomThemeColor(context: Context): Int {
|
||||
val typedValue = TypedValue()
|
||||
context.theme.resolveAttribute(android.R.attr.colorBackground, typedValue, true)
|
||||
return sharedPreferences.getInt(Keys.PREF_CUSTOM_THEME_COLOR, typedValue.data)
|
||||
}
|
||||
|
||||
/* Saves custom theme color */
|
||||
fun saveCustomThemeColor(color: Int) {
|
||||
sharedPreferences.edit { putInt(Keys.PREF_CUSTOM_THEME_COLOR, color) }
|
||||
}
|
||||
|
||||
/* Loads custom theme index (predefined color index) */
|
||||
fun loadCustomThemeIndex(): Int {
|
||||
return sharedPreferences.getInt(Keys.PREF_CUSTOM_THEME_INDEX, -1)
|
||||
}
|
||||
|
||||
/* Saves custom theme color and index together */
|
||||
fun saveCustomTheme(color: Int, index: Int) {
|
||||
sharedPreferences.edit {
|
||||
putInt(Keys.PREF_CUSTOM_THEME_COLOR, color)
|
||||
putInt(Keys.PREF_CUSTOM_THEME_INDEX, index)
|
||||
}
|
||||
}
|
||||
|
||||
/* Loads whether custom theme is enabled */
|
||||
fun loadCustomThemeEnabled(): Boolean {
|
||||
return sharedPreferences.getBoolean(Keys.PREF_CUSTOM_THEME_ENABLED, false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.michatec.radio.helpers
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import androidx.core.graphics.toColorInt
|
||||
|
||||
object ThemeHelper {
|
||||
fun getPredefinedColors(context: Context): List<Int> {
|
||||
val isDarkMode = (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
|
||||
return if (isDarkMode) {
|
||||
// Darker colors for dark mode background
|
||||
listOf(
|
||||
"#FF1D3E66".toColorInt(), // Default Dark
|
||||
"#FF3E1D1D".toColorInt(), // Red Dark
|
||||
"#FF1D3E3E".toColorInt(), // Teal Dark
|
||||
"#FF3E1D2E".toColorInt(), // Pink Dark
|
||||
"#FF001A33".toColorInt(), // Dark Blue Dark
|
||||
"#FF1D3E1D".toColorInt(), // Green Dark
|
||||
"#FF3E2E1D".toColorInt(), // Orange Dark
|
||||
"#FF2E1D1D".toColorInt(), // Brown Dark
|
||||
"#FF1D242E".toColorInt(), // Blue Grey Dark
|
||||
"#FF000000".toColorInt() // Black
|
||||
)
|
||||
} else {
|
||||
// Lighter colors for light mode background
|
||||
listOf(
|
||||
"#FFDAE2FF".toColorInt(), // Light Default
|
||||
"#FFFF897D".toColorInt(), // Light Red
|
||||
"#FF4DB6AC".toColorInt(), // Light Teal
|
||||
"#FFF48FB1".toColorInt(), // Light Pink
|
||||
"#FF90CAF9".toColorInt(), // Light Blue
|
||||
"#FFA5D6A7".toColorInt(), // Light Green
|
||||
"#FFFFAB91".toColorInt(), // Light Orange
|
||||
"#FFBCAAA4".toColorInt(), // Light Brown
|
||||
"#FFB0BEC5".toColorInt(), // Light Blue Grey
|
||||
"#FFFFFFFF".toColorInt() // White
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()) {
|
||||
|
||||
Reference in New Issue
Block a user