mirror of
https://github.com/Michatec/Radio.git
synced 2026-03-31 23:46:28 +02:00
Compare commits
8 Commits
c057aaaf2f
...
14.4
| Author | SHA1 | Date | |
|---|---|---|---|
| d270574365 | |||
| f96bdc6f07 | |||
| 7d63f16c2c | |||
| 47ff40e676 | |||
| dad709f5df | |||
| 89b13e152c | |||
| 1eefe1acc4 | |||
| 1fa8102e1c |
@@ -83,11 +83,12 @@ You can help out the radio-browser.info community by [adding the missing station
|
||||
|
||||
When **Edit Stations** is enabled:
|
||||
- Press **← (Left)** on the remote to open the detailed station editing area
|
||||
- Press **2** or **Back** to close the editing area
|
||||
- Press **3** or **Back** to close the editing area
|
||||
|
||||
**General TV** Controls:
|
||||
- Press **0** or **DEL** to remove the selected radio station
|
||||
- Press **1** or **SPACE** to make the selected radio station favourite
|
||||
- Press **2** or **PLUS** to reorder the selected radio station
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
@@ -6,8 +6,10 @@ import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.View
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import androidx.navigation.ui.AppBarConfiguration
|
||||
import androidx.navigation.ui.NavigationUI
|
||||
@@ -28,6 +30,7 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
/* Overrides onCreate from AppCompatActivity */
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
enableEdgeToEdge()
|
||||
setTheme(R.style.AppTheme)
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
@@ -49,11 +52,11 @@ class MainActivity : AppCompatActivity() {
|
||||
NavigationUI.setupWithNavController(toolbar, navController, appBarConfiguration)
|
||||
supportActionBar?.hide()
|
||||
|
||||
// TV-specific loading logic
|
||||
// TV-specific loading logic: Hide the overlay once the app is ready
|
||||
if (packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
findViewById<View>(R.id.loading_layout)?.visibility = View.GONE
|
||||
}, 1500)
|
||||
hideLoadingOverlay()
|
||||
}, 1200)
|
||||
} else {
|
||||
findViewById<View>(R.id.loading_layout)?.visibility = View.GONE
|
||||
}
|
||||
@@ -62,6 +65,18 @@ class MainActivity : AppCompatActivity() {
|
||||
PreferencesHelper.registerPreferenceChangeListener(sharedPreferenceChangeListener)
|
||||
}
|
||||
|
||||
/* Hides the loading/splash overlay */
|
||||
private fun hideLoadingOverlay() {
|
||||
findViewById<View>(R.id.loading_layout)?.let { overlay ->
|
||||
if (overlay.isVisible) {
|
||||
overlay.animate()
|
||||
.alpha(0f)
|
||||
.setDuration(500)
|
||||
.withEndAction { overlay.visibility = View.GONE }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Overrides onResume from AppCompatActivity */
|
||||
override fun onResume() {
|
||||
@@ -75,7 +90,6 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
/* Overrides onSupportNavigateUp from AppCompatActivity */
|
||||
override fun onSupportNavigateUp(): Boolean {
|
||||
// Taken from: https://developer.android.com/guide/navigation/navigation-ui#action_bar
|
||||
val navHostFragment = supportFragmentManager.findFragmentById(R.id.main_host_container) as NavHostFragment
|
||||
val navController = navHostFragment.navController
|
||||
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
|
||||
|
||||
@@ -22,6 +22,9 @@ import androidx.annotation.OptIn
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.media3.common.MediaItem
|
||||
@@ -167,8 +170,11 @@ class PlayerFragment : Fragment(),
|
||||
// hide action bar
|
||||
(activity as AppCompatActivity).supportActionBar?.hide()
|
||||
|
||||
// set the same background color of the player sheet for the navigation bar
|
||||
requireActivity().window.navigationBarColor = ContextCompat.getColor(requireActivity(), R.color.player_sheet_background)
|
||||
ViewCompat.setOnApplyWindowInsetsListener(layout.rootView) { v, insets ->
|
||||
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
v.updatePadding(bottom = systemBars.bottom)
|
||||
insets
|
||||
}
|
||||
// associate the ItemTouchHelper with the RecyclerView
|
||||
itemTouchHelper = ItemTouchHelper(ItemTouchHelperCallback())
|
||||
itemTouchHelper?.attachToRecyclerView(layout.recyclerView)
|
||||
@@ -242,7 +248,6 @@ class PlayerFragment : Fragment(),
|
||||
// load player state
|
||||
playerState = PreferencesHelper.loadPlayerState()
|
||||
// recreate player ui
|
||||
// setupPlaybackControls()
|
||||
updatePlayerViews()
|
||||
updateStationListState()
|
||||
togglePeriodicSleepTimerUpdateRequest()
|
||||
@@ -250,7 +255,7 @@ class PlayerFragment : Fragment(),
|
||||
observeCollectionViewModel()
|
||||
// handle navigation arguments
|
||||
handleNavigationArguments()
|
||||
// // handle start intent - if started via tap on rss link
|
||||
// handle start intent - if started via tap on rss link
|
||||
handleStartIntent()
|
||||
// start watching for changes in shared preferences
|
||||
PreferencesHelper.registerPreferenceChangeListener(this as SharedPreferences.OnSharedPreferenceChangeListener)
|
||||
@@ -405,6 +410,13 @@ class PlayerFragment : Fragment(),
|
||||
private fun setupController() {
|
||||
val controller: MediaController = this.controller ?: return
|
||||
controller.addListener(playerListener)
|
||||
|
||||
// Sync local playerState with actual controller state
|
||||
if (playerState.isPlaying != controller.isPlaying) {
|
||||
playerState.isPlaying = controller.isPlaying
|
||||
updatePlayerViews()
|
||||
}
|
||||
|
||||
requestMetadataUpdate()
|
||||
// handle start intent
|
||||
handleStartIntent()
|
||||
@@ -561,15 +573,27 @@ class PlayerFragment : Fragment(),
|
||||
|
||||
/* Handles this activity's start intent */
|
||||
private fun handleStartIntent() {
|
||||
if ((activity as Activity).intent.action != null) {
|
||||
when ((activity as Activity).intent.action) {
|
||||
Keys.ACTION_SHOW_PLAYER -> handleShowPlayer()
|
||||
Intent.ACTION_VIEW -> handleViewIntent()
|
||||
Keys.ACTION_START -> handleStartPlayer()
|
||||
val intent = (activity as Activity).intent
|
||||
if (intent.action != null && intent.action?.isNotEmpty() == true) {
|
||||
var handled = false
|
||||
when (intent.action) {
|
||||
Keys.ACTION_SHOW_PLAYER -> {
|
||||
handleShowPlayer()
|
||||
handled = true
|
||||
}
|
||||
Intent.ACTION_VIEW -> {
|
||||
handleViewIntent()
|
||||
handled = true
|
||||
}
|
||||
Keys.ACTION_START -> {
|
||||
handled = handleStartPlayer()
|
||||
}
|
||||
}
|
||||
if (handled) {
|
||||
// clear intent action to prevent double calls
|
||||
intent.action = ""
|
||||
}
|
||||
}
|
||||
// clear intent action to prevent double calls
|
||||
(activity as Activity).intent.action = ""
|
||||
}
|
||||
|
||||
|
||||
@@ -590,12 +614,12 @@ class PlayerFragment : Fragment(),
|
||||
val scheme: String = intentUri.scheme ?: String()
|
||||
// CASE: intent is a web link
|
||||
if (scheme.startsWith("http")) {
|
||||
Log.i(TAG, "Transistor was started to handle a web link.")
|
||||
Log.i(TAG, "Radio was started to handle a web link.")
|
||||
stationList.addAll(CollectionHelper.createStationsFromUrl(intentUri.toString()))
|
||||
}
|
||||
// CASE: intent is a local file
|
||||
else if (scheme.startsWith("content")) {
|
||||
Log.i(TAG, "Transistor was started to handle a local audio playlist.")
|
||||
Log.i(TAG, "Radio was started to handle a local audio playlist.")
|
||||
stationList.addAll(CollectionHelper.createStationListFromContentUri(activity as Context, intentUri))
|
||||
}
|
||||
withContext(Main) {
|
||||
@@ -612,17 +636,30 @@ class PlayerFragment : Fragment(),
|
||||
|
||||
|
||||
/* Handles START_PLAYER_SERVICE request from App Shortcut */
|
||||
private fun handleStartPlayer() {
|
||||
private fun handleStartPlayer(): Boolean {
|
||||
if (controller == null || collection.stations.isEmpty()) {
|
||||
return false
|
||||
}
|
||||
val intent: Intent = (activity as Activity).intent
|
||||
if (intent.hasExtra(Keys.EXTRA_START_LAST_PLAYED_STATION)) {
|
||||
controller?.play(activity as Context, CollectionHelper.getStation(collection, playerState.stationUuid))
|
||||
val station = CollectionHelper.getStation(collection, playerState.stationUuid)
|
||||
if (station.isValid()) {
|
||||
controller?.play(activity as Context, station)
|
||||
return true
|
||||
}
|
||||
} else if (intent.hasExtra(Keys.EXTRA_STATION_UUID)) {
|
||||
val uuid: String = intent.getStringExtra(Keys.EXTRA_STATION_UUID) ?: String()
|
||||
controller?.play(activity as Context, CollectionHelper.getStation(collection, uuid))
|
||||
val station = CollectionHelper.getStation(collection, uuid)
|
||||
if (station.isValid()) {
|
||||
controller?.play(activity as Context, station)
|
||||
return true
|
||||
}
|
||||
} else if (intent.hasExtra(Keys.EXTRA_STREAM_URI)) {
|
||||
val streamUri: String = intent.getStringExtra(Keys.EXTRA_STREAM_URI) ?: String()
|
||||
controller?.playStreamDirectly(streamUri)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -640,9 +677,9 @@ class PlayerFragment : Fragment(),
|
||||
collection = it
|
||||
// updates current station in player views
|
||||
playerState = PreferencesHelper.loadPlayerState()
|
||||
// // get station
|
||||
// get station
|
||||
val station: Station = CollectionHelper.getStation(collection, playerState.stationUuid)
|
||||
// // update player views
|
||||
// update player views
|
||||
layout.updatePlayerViews(activity as Context, station, playerState.isPlaying)
|
||||
// handle start intent
|
||||
handleStartIntent()
|
||||
|
||||
@@ -81,7 +81,8 @@ class PlayerService : MediaLibraryService() {
|
||||
|
||||
/* Overrides onDestroy from Service */
|
||||
override fun onDestroy() {
|
||||
// player.removeAnalyticsListener(analyticsListener)
|
||||
// Reset playing state in preferences
|
||||
PreferencesHelper.saveIsPlaying(false)
|
||||
player.removeListener(playerListener)
|
||||
player.release()
|
||||
mediaLibrarySession.release()
|
||||
@@ -241,11 +242,6 @@ class PlayerService : MediaLibraryService() {
|
||||
async(Dispatchers.Default) { FileHelper.readCollectionSuspended(context) }
|
||||
// wait for result and update collection
|
||||
collection = deferred.await()
|
||||
// // special case: trigger metadata view update for stations that have no metadata
|
||||
// if (player.isPlaying && station.name == getCurrentMetadata()) {
|
||||
// station = CollectionHelper.getStation(collection, station.uuid)
|
||||
// updateMetadata(null)
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -429,7 +425,6 @@ class PlayerService : MediaLibraryService() {
|
||||
currentMediaId,
|
||||
isPlaying
|
||||
)
|
||||
//updatePlayerState(station, playbackState)
|
||||
|
||||
if (isPlaying) {
|
||||
// playback is active
|
||||
@@ -506,10 +501,6 @@ class PlayerService : MediaLibraryService() {
|
||||
// try to reconnect every 5 seconds - up to 20 times
|
||||
if (loadErrorInfo.errorCount <= Keys.DEFAULT_MAX_RECONNECTION_COUNT && loadErrorInfo.exception is HttpDataSource.HttpDataSourceException) {
|
||||
return Keys.RECONNECTION_WAIT_INTERVAL
|
||||
// } else {
|
||||
// CoroutineScope(Main).launch {
|
||||
// player.stop()
|
||||
// }
|
||||
}
|
||||
return C.TIME_UNSET
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import com.michatec.radio.dialogs.ErrorDialog
|
||||
import com.michatec.radio.dialogs.ThemeSelectionDialog
|
||||
import com.michatec.radio.dialogs.YesNoDialog
|
||||
import com.michatec.radio.helpers.*
|
||||
import com.michatec.radio.helpers.AppThemeHelper.getColor
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -46,7 +45,6 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
||||
(activity as AppCompatActivity).supportActionBar?.show()
|
||||
(activity as AppCompatActivity).supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
(activity as AppCompatActivity).supportActionBar?.title = getString(R.string.fragment_settings_title)
|
||||
requireActivity().window.navigationBarColor = getColor(requireContext(), android.R.attr.colorBackground)
|
||||
}
|
||||
|
||||
/* Overrides onCreatePreferences from PreferenceFragmentCompat */
|
||||
@@ -245,7 +243,7 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
||||
val preferenceCategoryMaintenance = PreferenceCategory(activity as Context)
|
||||
preferenceCategoryMaintenance.title = getString(R.string.pref_maintenance_title)
|
||||
preferenceCategoryMaintenance.contains(preferenceUpdateStationImages)
|
||||
// preferenceCategoryMaintenance.contains(preferenceUpdateCollection)
|
||||
preferenceCategoryMaintenance.contains(preferenceUpdateCollection)
|
||||
|
||||
val preferenceCategoryImportExport = PreferenceCategory(activity as Context)
|
||||
preferenceCategoryImportExport.title = getString(R.string.pref_backup_import_export_title)
|
||||
@@ -549,7 +547,6 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
||||
requestRestoreCollectionLauncher.launch(intent)
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "Unable to open file picker for ZIP.\n$exception")
|
||||
// Toast.makeText(activity as Context, R.string.toast_message_install_file_helper, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.michatec.radio.collection
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.content.res.ColorStateList
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.KeyEvent
|
||||
@@ -14,8 +15,8 @@ import android.widget.ImageView
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.cardview.widget.CardView
|
||||
import androidx.constraintlayout.widget.Group
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
@@ -25,6 +26,7 @@ import androidx.navigation.findNavController
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import com.michatec.radio.Keys
|
||||
@@ -54,6 +56,7 @@ class CollectionAdapter(
|
||||
private var expandedStationUuid: String = PreferencesHelper.loadStationListStreamUuid()
|
||||
private var expandedStationPosition: Int = -1
|
||||
var isExpandedForEdit: Boolean = false
|
||||
private var reorderStationUuid: String = ""
|
||||
|
||||
|
||||
/* Listener Interface */
|
||||
@@ -184,6 +187,15 @@ class CollectionAdapter(
|
||||
setPlaybackProgress(stationViewHolder, station)
|
||||
setDownloadProgress(stationViewHolder, station)
|
||||
|
||||
// highlight if reordering
|
||||
if (reorderStationUuid == station.uuid) {
|
||||
stationViewHolder.stationCardView.setStrokeColor(
|
||||
ColorStateList.valueOf(
|
||||
ContextCompat.getColor(context, R.color.cardview_reordering)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// show / hide edit views
|
||||
when (expandedStationPosition) {
|
||||
// show edit views
|
||||
@@ -310,6 +322,21 @@ class CollectionAdapter(
|
||||
}
|
||||
}
|
||||
|
||||
/* Shows / hides the reorder highlight for a station */
|
||||
private fun toggleReorderMode(position: Int, stationUuid: String) {
|
||||
if (reorderStationUuid == stationUuid) {
|
||||
reorderStationUuid = ""
|
||||
saveCollectionAfterDragDrop()
|
||||
} else {
|
||||
// collapse edit views if necessary
|
||||
if (isExpandedForEdit) {
|
||||
toggleEditViews(expandedStationPosition, expandedStationUuid)
|
||||
}
|
||||
reorderStationUuid = stationUuid
|
||||
}
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
|
||||
|
||||
/* Shows / hides the edit view for a station */
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
@@ -345,12 +372,16 @@ class CollectionAdapter(
|
||||
when (station.starred) {
|
||||
true -> {
|
||||
if (station.imageColor != -1) {
|
||||
// stationViewHolder.stationCardView.setCardBackgroundColor(station.imageColor)
|
||||
stationViewHolder.stationStarredView.setColorFilter(station.imageColor)
|
||||
} else {
|
||||
stationViewHolder.stationStarredView.clearColorFilter()
|
||||
}
|
||||
stationViewHolder.stationStarredView.isVisible = true
|
||||
}
|
||||
false -> stationViewHolder.stationStarredView.isGone = true
|
||||
false -> {
|
||||
stationViewHolder.stationStarredView.clearColorFilter()
|
||||
stationViewHolder.stationStarredView.isGone = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -378,6 +409,7 @@ class CollectionAdapter(
|
||||
false -> stationViewHolder.playButtonView.visibility = View.INVISIBLE
|
||||
}
|
||||
stationViewHolder.stationCardView.setOnClickListener {
|
||||
if (reorderStationUuid.isNotEmpty()) return@setOnClickListener
|
||||
if (expandedStationPosition == stationViewHolder.bindingAdapterPosition) return@setOnClickListener
|
||||
collectionAdapterListener.onPlayButtonTapped(station.uuid)
|
||||
}
|
||||
@@ -394,9 +426,35 @@ class CollectionAdapter(
|
||||
collectionAdapterListener.onPlayButtonTapped(station.uuid)
|
||||
}
|
||||
|
||||
// TV improvement: Allow opening edit view with DPAD_LEFT
|
||||
// TV improvement: Allow reordering with DPAD
|
||||
stationViewHolder.stationCardView.setOnKeyListener { _, keyCode, event ->
|
||||
if (event.action == KeyEvent.ACTION_DOWN) {
|
||||
// Reorder mode handling
|
||||
if (reorderStationUuid == station.uuid) {
|
||||
when (keyCode) {
|
||||
KeyEvent.KEYCODE_DPAD_UP -> {
|
||||
val currentPos = stationViewHolder.bindingAdapterPosition
|
||||
if (currentPos > 0) {
|
||||
onItemMove(currentPos, currentPos - 1)
|
||||
}
|
||||
return@setOnKeyListener true
|
||||
}
|
||||
KeyEvent.KEYCODE_DPAD_DOWN -> {
|
||||
val currentPos = stationViewHolder.bindingAdapterPosition
|
||||
if (currentPos < collection.stations.size - 1) {
|
||||
onItemMove(currentPos, currentPos + 1)
|
||||
}
|
||||
return@setOnKeyListener true
|
||||
}
|
||||
KeyEvent.KEYCODE_NUMPAD_2, KeyEvent.KEYCODE_2, KeyEvent.KEYCODE_PLUS,
|
||||
KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_DPAD_CENTER -> {
|
||||
toggleReorderMode(stationViewHolder.bindingAdapterPosition, station.uuid)
|
||||
return@setOnKeyListener true
|
||||
}
|
||||
else -> return@setOnKeyListener true // Consume other keys in reorder mode
|
||||
}
|
||||
}
|
||||
|
||||
when (keyCode) {
|
||||
KeyEvent.KEYCODE_DPAD_LEFT -> {
|
||||
if (editStationsEnabled && expandedStationPosition != stationViewHolder.bindingAdapterPosition) {
|
||||
@@ -405,7 +463,7 @@ class CollectionAdapter(
|
||||
return@setOnKeyListener true
|
||||
}
|
||||
}
|
||||
KeyEvent.KEYCODE_NUMPAD_2, KeyEvent.KEYCODE_2, KeyEvent.KEYCODE_BACK -> {
|
||||
KeyEvent.KEYCODE_NUMPAD_3, KeyEvent.KEYCODE_3, KeyEvent.KEYCODE_BACK -> {
|
||||
if (expandedStationPosition == stationViewHolder.bindingAdapterPosition) {
|
||||
val position: Int = stationViewHolder.bindingAdapterPosition
|
||||
toggleEditViews(position, station.uuid)
|
||||
@@ -420,6 +478,10 @@ class CollectionAdapter(
|
||||
toggleStarredStation(context, stationViewHolder.bindingAdapterPosition)
|
||||
return@setOnKeyListener true
|
||||
}
|
||||
KeyEvent.KEYCODE_NUMPAD_2, KeyEvent.KEYCODE_2, KeyEvent.KEYCODE_PLUS -> {
|
||||
toggleReorderMode(stationViewHolder.bindingAdapterPosition, station.uuid)
|
||||
return@setOnKeyListener true
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
@@ -703,7 +765,7 @@ class CollectionAdapter(
|
||||
*/
|
||||
private class StationViewHolder(stationCardLayout: View) :
|
||||
RecyclerView.ViewHolder(stationCardLayout) {
|
||||
val stationCardView: CardView = stationCardLayout.findViewById(R.id.station_card)
|
||||
val stationCardView: MaterialCardView = stationCardLayout.findViewById(R.id.station_card)
|
||||
val stationImageView: ImageView = stationCardLayout.findViewById(R.id.station_icon)
|
||||
val stationNameView: TextView = stationCardLayout.findViewById(R.id.station_name)
|
||||
val stationStarredView: ImageView = stationCardLayout.findViewById(R.id.starred_icon)
|
||||
|
||||
@@ -27,28 +27,6 @@ class ThemeSelectionDialog(private var themeSelectionDialogListener: ThemeSelect
|
||||
private lateinit var dialog: AlertDialog
|
||||
|
||||
|
||||
/* Update radio buttons to reflect current theme */
|
||||
private fun updateRadioButtons(
|
||||
context: Context,
|
||||
radioFollowSystem: RadioButton,
|
||||
radioLight: RadioButton,
|
||||
radioDark: RadioButton
|
||||
) {
|
||||
val currentTheme = AppThemeHelper.getCurrentTheme(context)
|
||||
when (currentTheme) {
|
||||
context.getString(R.string.pref_theme_selection_mode_device_default) -> {
|
||||
radioFollowSystem.isChecked = true
|
||||
}
|
||||
context.getString(R.string.pref_theme_selection_mode_light) -> {
|
||||
radioLight.isChecked = true
|
||||
}
|
||||
context.getString(R.string.pref_theme_selection_mode_dark) -> {
|
||||
radioDark.isChecked = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Construct and show dialog */
|
||||
fun show(context: Context) {
|
||||
// prepare dialog builder
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package com.michatec.radio.helpers
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.TypedArray
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import com.michatec.radio.Keys
|
||||
import com.michatec.radio.R
|
||||
@@ -19,8 +15,6 @@ object AppThemeHelper {
|
||||
/* Define log tag */
|
||||
private val TAG: String = AppThemeHelper::class.java.simpleName
|
||||
|
||||
private val sTypedValue = TypedValue()
|
||||
|
||||
/* Sets app theme */
|
||||
fun setTheme(nightModeState: String) {
|
||||
when (nightModeState) {
|
||||
@@ -62,14 +56,4 @@ object AppThemeHelper {
|
||||
else -> context.getString(R.string.pref_theme_selection_mode_device_default)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ColorInt
|
||||
fun getColor(context: Context, @AttrRes resource: Int): Int {
|
||||
val a: TypedArray = context.obtainStyledAttributes(sTypedValue.data, intArrayOf(resource))
|
||||
val color = a.getColor(0, 0)
|
||||
a.recycle()
|
||||
return color
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ object BackupHelper {
|
||||
Snackbar.make(view, R.string.toastmessage_restored, Snackbar.LENGTH_LONG).show()
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
// bypass "ZipException" for Android 14 or above applications when zip file names contain ".." or start with "/"
|
||||
// bypass "ZipException" for Android 14 or above applications when zip file names contain "." or start with "/"
|
||||
dalvik.system.ZipPathValidator.clearCallback()
|
||||
}
|
||||
|
||||
|
||||
@@ -299,7 +299,7 @@ object CollectionHelper {
|
||||
}
|
||||
|
||||
|
||||
/* Returns the children stations under under root (simple media library structure: root > stations) */
|
||||
/* Returns the children stations under root (simple media library structure: root > stations) */
|
||||
fun getChildren(context: Context, collection: Collection): List<MediaItem> {
|
||||
val mediaItems: MutableList<MediaItem> = mutableListOf()
|
||||
collection.stations.forEach { station ->
|
||||
|
||||
@@ -17,14 +17,14 @@ object DateTimeHelper {
|
||||
|
||||
|
||||
/* Main class variables */
|
||||
private const val pattern: String = "EEE, dd MMM yyyy HH:mm:ss Z"
|
||||
private val dateFormat: SimpleDateFormat = SimpleDateFormat(pattern, Locale.ENGLISH)
|
||||
private const val PATTERN: String = "EEE, dd MMM yyyy HH:mm:ss Z"
|
||||
private val dateFormat: SimpleDateFormat = SimpleDateFormat(PATTERN, Locale.ENGLISH)
|
||||
|
||||
|
||||
/* Converts RFC 2822 string representation of a date to DATE */
|
||||
fun convertFromRfc2822(dateString: String): Date {
|
||||
val date: Date = try {
|
||||
// parse date string using standard pattern
|
||||
// parse date string using standard PATTERN
|
||||
dateFormat.parse((dateString)) ?: Keys.DEFAULT_DATE
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Unable to parse. Trying an alternative Date format. $e")
|
||||
@@ -37,7 +37,7 @@ object DateTimeHelper {
|
||||
|
||||
/* Converts a DATE to its RFC 2822 string representation */
|
||||
fun convertToRfc2822(date: Date): String {
|
||||
val dateFormat = SimpleDateFormat(pattern, Locale.ENGLISH)
|
||||
val dateFormat = SimpleDateFormat(PATTERN, Locale.ENGLISH)
|
||||
return dateFormat.format(date)
|
||||
}
|
||||
|
||||
|
||||
@@ -370,7 +370,7 @@ object DownloadHelper {
|
||||
private fun determineAllowedNetworkTypes(type: Int, ignoreWifiRestriction: Boolean): Int {
|
||||
var allowedNetworkTypes: Int =
|
||||
(DownloadManager.Request.NETWORK_WIFI or DownloadManager.Request.NETWORK_MOBILE)
|
||||
// restrict download of audio files to WiFi if necessary
|
||||
// restrict download of audio files to Wi-Fi if necessary
|
||||
if (type == Keys.FILE_TYPE_AUDIO) {
|
||||
if (!ignoreWifiRestriction && !PreferencesHelper.downloadOverMobile()) {
|
||||
allowedNetworkTypes = DownloadManager.Request.NETWORK_WIFI
|
||||
|
||||
@@ -115,7 +115,7 @@ object NetworkHelper {
|
||||
}
|
||||
|
||||
|
||||
/* Creates a http connection from given url string */
|
||||
/* Creates an http connection from given url string */
|
||||
private fun createConnection(urlString: String, redirectCount: Int = 0): HttpURLConnection? {
|
||||
var connection: HttpURLConnection? = null
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ object ShortcutHelper {
|
||||
|
||||
/* Places shortcut on Home screen */
|
||||
fun placeShortcut(context: Context, station: Station) {
|
||||
// credit: https://medium.com/@BladeCoder/using-support-library-26-0-0-you-can-do-bb75911e01e8
|
||||
if (ShortcutManagerCompat.isRequestPinShortcutSupported(context)) {
|
||||
val shortcut: ShortcutInfoCompat = ShortcutInfoCompat.Builder(context, station.name)
|
||||
.setShortLabel(station.name)
|
||||
|
||||
@@ -128,7 +128,6 @@ data class LayoutHolder(var rootView: View) {
|
||||
if (!isPlaying) {
|
||||
metadataView?.text = station.name
|
||||
sheetMetadataHistoryView?.text = station.name
|
||||
// sheetMetadataHistoryView.isSelected = true
|
||||
}
|
||||
|
||||
// update name
|
||||
@@ -289,11 +288,9 @@ data class LayoutHolder(var rootView: View) {
|
||||
val animatedVectorDrawable = playButtonView.drawable as? AnimatedVectorDrawable
|
||||
animatedVectorDrawable?.start()
|
||||
sheetSleepTimerStartButtonView?.isVisible = true
|
||||
// bufferingIndicator.isVisible = false
|
||||
} else {
|
||||
playButtonView.setImageResource(R.drawable.ic_player_play_symbol_42dp)
|
||||
sheetSleepTimerStartButtonView?.isVisible = false
|
||||
// bufferingIndicator.isVisible = isBuffering
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,6 @@
|
||||
<item
|
||||
android:width="160dp"
|
||||
android:height="160dp"
|
||||
android:gravity="center">
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/ic_launcher" />
|
||||
</item>
|
||||
android:gravity="center"
|
||||
android:drawable="@mipmap/ic_launcher" />
|
||||
</layer-list>
|
||||
@@ -4,6 +4,7 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?android:attr/colorBackground"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
|
||||
@@ -32,4 +32,5 @@
|
||||
<color name="player_sheet_text_main">@android:color/system_neutral1_50</color>
|
||||
<color name="player_sheet_icon">@android:color/system_neutral1_50</color>
|
||||
|
||||
<color name="cardview_reordering">@android:color/system_accent1_100</color>
|
||||
</resources>
|
||||
@@ -31,9 +31,6 @@
|
||||
<!-- Don't show light status bar -->
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
|
||||
<!-- Set Navigation Bar color -->
|
||||
<item name="android:navigationBarColor">@color/player_sheet_background</item>
|
||||
|
||||
<!-- Set splash screen icon background color -->
|
||||
<item name="android:windowSplashScreenIconBackgroundColor">@color/splashBackgroundColor</item>
|
||||
</style>
|
||||
|
||||
@@ -31,4 +31,5 @@
|
||||
<color name="player_sheet_text_main">#FFFFFFFF</color>
|
||||
<color name="player_sheet_icon">#FFFFFFFF</color>
|
||||
|
||||
<color name="cardview_reordering">#FFDAE2FF</color>
|
||||
</resources>
|
||||
@@ -3,6 +3,5 @@
|
||||
<style name="SplashTheme" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<item name="android:windowBackground">@drawable/splash_screen</item>
|
||||
<item name="android:statusBarColor">@color/splashBackgroundColor</item>
|
||||
<item name="android:navigationBarColor">@color/splashBackgroundColor</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -1,29 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="AppTheme" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<!-- Set AppCompat’s colors -->
|
||||
<item name="colorPrimary">#FF495D92</item>
|
||||
<item name="colorAccent">#FF495D92</item>
|
||||
<item name="android:textColorHighlight">#FF495D92</item>
|
||||
<item name="colorControlHighlight">#33000000</item>
|
||||
<item name="android:colorControlHighlight">#33000000</item>
|
||||
<item name="android:colorFocusedHighlight">#80000000</item>
|
||||
|
||||
<!-- Do not use primary colored elevation overlays to present a visual hierarchy - TOO COLORFUL -->
|
||||
<item name="elevationOverlayEnabled">false</item>
|
||||
|
||||
<!-- Switch Theming -->
|
||||
<item name="switchPreferenceCompatStyle">@style/Preference.SwitchPreferenceCompat.Material3</item>
|
||||
|
||||
<!-- Material Alert Dialog Theming -->
|
||||
<item name="alertDialogTheme">@style/ThemeOverlay.App.AlertDialogTheme</item>
|
||||
|
||||
<!-- Use "light" Status Bar -->
|
||||
<item name="android:windowLightStatusBar">true</item>
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
|
||||
<!-- Use "light" Navigation Bar -->
|
||||
<item name="android:windowLightNavigationBar">true</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -37,4 +37,5 @@
|
||||
<color name="default_neutral_medium_light">@android:color/system_neutral1_300</color>
|
||||
<color name="default_neutral_dark">@android:color/system_neutral1_600</color>
|
||||
|
||||
<color name="cardview_reordering">@android:color/system_accent1_900</color>
|
||||
</resources>
|
||||
@@ -39,5 +39,5 @@
|
||||
<color name="default_neutral_85percent">#D9313033</color>
|
||||
|
||||
<color name="splashBackgroundColor">#FF1D3E66</color>
|
||||
|
||||
<color name="cardview_reordering">#161C2C</color>
|
||||
</resources>
|
||||
@@ -141,5 +141,7 @@
|
||||
<string name="snackbar_github_update_check_url" translatable="false">https://api.github.com/repos/michatec/Radio/releases/latest</string>
|
||||
<string name="app_name" translatable="false">Radio</string>
|
||||
<string name="icon_launcher" translatable="false">Icon launcher.</string>
|
||||
|
||||
<!-- Extras -->
|
||||
<string name="loading">Loading...</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user