35 Commits

Author SHA1 Message Date
Michachatz
95bf0e5c56 Merge pull request #38 from Michatec/renovate/kotlin-monorepo
Update plugin org.jetbrains.kotlin.android to v2.3.10
2026-02-08 12:52:02 +01:00
Michachatz
dddd180136 Merge pull request #39 from Michatec/renovate/androidx.media3-media3-datasource-okhttp-1.x
Update dependency androidx.media3:media3-datasource-okhttp to v1.9.2
2026-02-08 12:51:50 +01:00
Michachatz
46ddd4feca Merge pull request #40 from Michatec/renovate/androidx.media3-media3-exoplayer-1.x
Update dependency androidx.media3:media3-exoplayer to v1.9.2
2026-02-08 12:51:37 +01:00
renovate[bot]
e10c8ed68f Update dependency androidx.media3:media3-exoplayer to v1.9.2 2026-02-08 11:51:24 +00:00
Michachatz
6563820a00 Merge pull request #41 from Michatec/renovate/androidx.media3-media3-exoplayer-hls-1.x
Update dependency androidx.media3:media3-exoplayer-hls to v1.9.2
2026-02-08 12:51:04 +01:00
renovate[bot]
594801238a Update dependency androidx.media3:media3-exoplayer-hls to v1.9.2 2026-02-08 11:50:10 +00:00
renovate[bot]
8a1e0ef6c2 Update dependency androidx.media3:media3-datasource-okhttp to v1.9.2 2026-02-08 11:50:07 +00:00
Michachatz
053db2abaa Merge pull request #42 from Michatec/renovate/androidx.media3-media3-session-1.x
Update dependency androidx.media3:media3-session to v1.9.2
2026-02-08 12:49:48 +01:00
renovate[bot]
7e5d5eb6c1 Update dependency androidx.media3:media3-session to v1.9.2 2026-02-06 13:11:45 +00:00
renovate[bot]
1e1efd422c Update plugin org.jetbrains.kotlin.android to v2.3.10 2026-02-05 10:55:36 +00:00
Michachatz
2c1913896d Merge pull request #29 from Michatec/renovate/androidx.media3-media3-datasource-okhttp-1.x
Update dependency androidx.media3:media3-datasource-okhttp to v1.9.1
2026-01-30 18:56:34 +01:00
Michachatz
026e18ae22 Merge pull request #30 from Michatec/renovate/androidx.media3-media3-exoplayer-1.x
Update dependency androidx.media3:media3-exoplayer to v1.9.1
2026-01-30 18:56:18 +01:00
renovate[bot]
3b6ebe5e1b Update dependency androidx.media3:media3-exoplayer to v1.9.1 2026-01-30 17:55:55 +00:00
Michachatz
26eab4c64c Merge pull request #31 from Michatec/renovate/androidx.media3-media3-exoplayer-hls-1.x
Update dependency androidx.media3:media3-exoplayer-hls to v1.9.1
2026-01-30 18:55:43 +01:00
renovate[bot]
75b3deb210 Update dependency androidx.media3:media3-exoplayer-hls to v1.9.1 2026-01-30 17:55:23 +00:00
renovate[bot]
96389a5c81 Update dependency androidx.media3:media3-datasource-okhttp to v1.9.1 2026-01-30 17:55:19 +00:00
Michachatz
1544c07938 Merge pull request #32 from Michatec/renovate/androidx.media3-media3-session-1.x
Update dependency androidx.media3:media3-session to v1.9.1
2026-01-30 18:54:56 +01:00
Michachatz
65e9d1fed4 Merge pull request #33 from Michatec/renovate/androidx.navigation-navigation-fragment-ktx-2.x
Update dependency androidx.navigation:navigation-fragment-ktx to v2.9.7
2026-01-30 18:54:45 +01:00
renovate[bot]
9bfc1661b8 Update dependency androidx.navigation:navigation-fragment-ktx to v2.9.7 2026-01-30 17:54:21 +00:00
Michachatz
9a379675bf Merge pull request #34 from Michatec/renovate/androidx.navigation-navigation-ui-ktx-2.x
Update dependency androidx.navigation:navigation-ui-ktx to v2.9.7
2026-01-30 18:53:49 +01:00
renovate[bot]
529e359f7f Update dependency androidx.navigation:navigation-ui-ktx to v2.9.7 2026-01-30 17:52:58 +00:00
Michachatz
fc3a1f767a Merge pull request #35 from Michatec/renovate/androidx.activity-activity-ktx-1.x
Update dependency androidx.activity:activity-ktx to v1.12.3
2026-01-30 18:52:45 +01:00
Michachatz
9197e5c2a9 Merge pull request #36 from Michatec/renovate/androidx.work-work-runtime-ktx-2.x
Update dependency androidx.work:work-runtime-ktx to v2.11.1
2026-01-30 18:52:34 +01:00
Michachatz
5920327283 Merge pull request #37 from Michatec/renovate/gradle-9.x
Update Gradle to v9.3.1
2026-01-30 18:52:22 +01:00
renovate[bot]
769cbb505f Update Gradle to v9.3.1 2026-01-29 16:34:13 +00:00
renovate[bot]
1f5d61759c Update dependency androidx.work:work-runtime-ktx to v2.11.1 2026-01-29 04:41:58 +00:00
renovate[bot]
5b1d6d96c7 Update dependency androidx.activity:activity-ktx to v1.12.3 2026-01-29 04:41:54 +00:00
renovate[bot]
606154cfb3 Update dependency androidx.media3:media3-session to v1.9.1 2026-01-26 17:46:05 +00:00
Michatec
9b5c7e3c04 - New version Initialized 2026-01-25 17:07:30 +01:00
Michatec
4466654e92 - Fix unused import 2026-01-25 16:33:45 +01:00
Michatec
b2de7bd534 - Progress Bar added
- CollectionAdapter.kt updated
- File download optimized
- Housekeeping updated
2026-01-25 16:33:17 +01:00
Michatec
d3dfcb98f9 - Layout Changes
- HLS media extraction
2026-01-25 16:08:22 +01:00
Michachatz
0adb906438 Aktualisieren von provider_paths.xml 2026-01-22 06:55:40 +01:00
Michachatz
49e63d3aaa Aktualisieren von shortcuts.xml 2026-01-22 06:55:08 +01:00
Michatec
f892a137ce - Fix .yml 2026-01-21 18:14:38 +01:00
14 changed files with 140 additions and 69 deletions

View File

@@ -51,6 +51,10 @@ jobs:
- name: Check APK path - name: Check APK path
run: ls -R app/build/outputs/apk run: ls -R app/build/outputs/apk
- name: Zipalign APK
run: |
/usr/local/lib/android/sdk/build-tools/34.0.0/zipalign -v -p 4 ${{ env.APK_PATH }} app-release-aligned.apk
- name: Sign APK - name: Sign APK
env: env:
SIGN_CERT: ${{ secrets.SIGN_CERT }} SIGN_CERT: ${{ secrets.SIGN_CERT }}
@@ -58,13 +62,8 @@ jobs:
run: | run: |
echo "$SIGN_CERT" | base64 -d > cert.der echo "$SIGN_CERT" | base64 -d > cert.der
echo "$SIGN_KEY" | base64 -d > key.der echo "$SIGN_KEY" | base64 -d > key.der
mv ${{ env.APK_PATH }} app-release.apk ${{ env.APKSIGNER }} sign --key key.der --cert cert.der app-release-aligned.apk
${{ env.APKSIGNER }} sign --key key.der --cert cert.der app-release.apk
rm cert.der key.der rm cert.der key.der
- name: Zipalign APK
run: |
/usr/local/lib/android/sdk/build-tools/34.0.0/zipalign -v -p 4 app-release.apk app-release-aligned.apk
mv app-release-aligned.apk app-release.apk mv app-release-aligned.apk app-release.apk
- name: Upload artifact - name: Upload artifact

View File

@@ -19,8 +19,8 @@ android {
applicationId 'com.michatec.radio' applicationId 'com.michatec.radio'
minSdk 28 minSdk 28
targetSdk 36 targetSdk 36
versionCode 140 versionCode 141
versionName '14' versionName '14.1'
resourceConfigurations += ['en', 'de', 'el', 'nl', 'pl', 'ru','uk', 'ja', 'da', 'fr'] resourceConfigurations += ['en', 'de', 'el', 'nl', 'pl', 'ru','uk', 'ja', 'da', 'fr']
} }
@@ -60,17 +60,17 @@ dependencies {
// AndroidX Stuff // // AndroidX Stuff //
implementation 'androidx.core:core-ktx:1.17.0' implementation 'androidx.core:core-ktx:1.17.0'
implementation 'androidx.activity:activity-ktx:1.12.2' implementation 'androidx.activity:activity-ktx:1.12.3'
implementation 'androidx.palette:palette-ktx:1.0.0' implementation 'androidx.palette:palette-ktx:1.0.0'
implementation 'androidx.preference:preference-ktx:1.2.1' implementation 'androidx.preference:preference-ktx:1.2.1'
implementation 'androidx.media:media:1.7.1' implementation 'androidx.media:media:1.7.1'
implementation 'androidx.media3:media3-exoplayer:1.9.0' implementation 'androidx.media3:media3-exoplayer:1.9.2'
implementation 'androidx.media3:media3-exoplayer-hls:1.9.0' implementation 'androidx.media3:media3-exoplayer-hls:1.9.2'
implementation 'androidx.media3:media3-session:1.9.0' implementation 'androidx.media3:media3-session:1.9.2'
implementation 'androidx.media3:media3-datasource-okhttp:1.9.0' implementation 'androidx.media3:media3-datasource-okhttp:1.9.2'
implementation 'androidx.navigation:navigation-fragment-ktx:2.9.6' implementation 'androidx.navigation:navigation-fragment-ktx:2.9.7'
implementation 'androidx.navigation:navigation-ui-ktx:2.9.6' implementation 'androidx.navigation:navigation-ui-ktx:2.9.7'
implementation 'androidx.work:work-runtime-ktx:2.11.0' implementation 'androidx.work:work-runtime-ktx:2.11.1'
// Volley HTTP request // // Volley HTTP request //
implementation 'com.android.volley:volley:1.2.1' implementation 'com.android.volley:volley:1.2.1'

View File

@@ -24,7 +24,6 @@ import androidx.navigation.ui.NavigationUI
import androidx.navigation.ui.navigateUp import androidx.navigation.ui.navigateUp
import com.michatec.radio.helpers.AppThemeHelper import com.michatec.radio.helpers.AppThemeHelper
import com.michatec.radio.helpers.FileHelper import com.michatec.radio.helpers.FileHelper
import com.michatec.radio.helpers.ImportHelper
import com.michatec.radio.helpers.PreferencesHelper import com.michatec.radio.helpers.PreferencesHelper
@@ -41,18 +40,6 @@ class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
// house-keeping: determine if edit stations is enabled by default todo: remove in 2023
if (PreferencesHelper.isHouseKeepingNecessary()) {
// house-keeping 1: remove hard coded default image
ImportHelper.removeDefaultStationImageUris(this)
// house-keeping 2: if existing user detected, enable Edit Stations by default
if (PreferencesHelper.loadCollectionSize() != -1) {
// existing user detected - enable Edit Stations by default
PreferencesHelper.saveEditStationsEnabled(true)
}
PreferencesHelper.saveHouseKeepingNecessaryState()
}
// set up views // set up views
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)

View File

@@ -24,6 +24,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import android.widget.ImageView import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.cardview.widget.CardView import androidx.cardview.widget.CardView
@@ -193,6 +194,8 @@ class CollectionAdapter(
setStationImage(stationViewHolder, station) setStationImage(stationViewHolder, station)
setStationButtons(stationViewHolder, station) setStationButtons(stationViewHolder, station)
setEditViews(stationViewHolder, station) setEditViews(stationViewHolder, station)
setPlaybackProgress(stationViewHolder, station)
setDownloadProgress(stationViewHolder, station)
// show / hide edit views // show / hide edit views
when (expandedStationPosition) { when (expandedStationPosition) {
@@ -248,6 +251,28 @@ class CollectionAdapter(
} }
/* Sets the playback progress view */
private fun setPlaybackProgress(stationViewHolder: StationViewHolder, station: Station) {
if (station.bufferingProgress > 0) {
stationViewHolder.bufferingProgress.progress = station.bufferingProgress
stationViewHolder.bufferingProgress.isVisible = true
} else {
stationViewHolder.bufferingProgress.isGone = true
}
}
/* Sets the download progress view */
private fun setDownloadProgress(stationViewHolder: StationViewHolder, station: Station) {
if (station.downloadProgress > 0) {
stationViewHolder.downloadProgress.progress = station.downloadProgress
stationViewHolder.downloadProgress.isVisible = true
} else {
stationViewHolder.downloadProgress.isGone = true
}
}
/* Sets the edit views */ /* Sets the edit views */
private fun setEditViews(stationViewHolder: StationViewHolder, station: Station) { private fun setEditViews(stationViewHolder: StationViewHolder, station: Station) {
stationViewHolder.stationNameEditView.setText(station.name, TextView.BufferType.EDITABLE) stationViewHolder.stationNameEditView.setText(station.name, TextView.BufferType.EDITABLE)
@@ -471,24 +496,25 @@ class CollectionAdapter(
} else if (holder is StationViewHolder) { } else if (holder is StationViewHolder) {
// get station from position // get station from position
collection.stations[holder.bindingAdapterPosition] val station: Station = collection.stations[holder.bindingAdapterPosition]
for (data in payloads) { for (data in payloads) {
when (data as Int) { when (data as Int) {
Keys.HOLDER_UPDATE_COVER -> { Keys.HOLDER_UPDATE_COVER -> {
// todo implement setStationImage(holder, station)
setStarredIcon(holder, station)
} }
Keys.HOLDER_UPDATE_NAME -> { Keys.HOLDER_UPDATE_NAME -> {
// todo implement setStationName(holder, station)
} }
Keys.HOLDER_UPDATE_PLAYBACK_STATE -> { Keys.HOLDER_UPDATE_PLAYBACK_STATE -> {
// todo implement setStationButtons(holder, station)
} }
Keys.HOLDER_UPDATE_PLAYBACK_PROGRESS -> { Keys.HOLDER_UPDATE_PLAYBACK_PROGRESS -> {
// todo implement setPlaybackProgress(holder, station)
} }
Keys.HOLDER_UPDATE_DOWNLOAD_STATE -> { Keys.HOLDER_UPDATE_DOWNLOAD_STATE -> {
// todo implement setDownloadProgress(holder, station)
} }
} }
} }
@@ -575,21 +601,6 @@ class CollectionAdapter(
} }
// /* Initiates update of a station's information */ // todo move to CollectionHelper
// private fun updateStation(context: Context, station: Station) {
// if (station.radioBrowserStationUuid.isNotEmpty()) {
// // get updated station from radio browser - results are handled by onRadioBrowserSearchResults
// val radioBrowserSearch: RadioBrowserSearch = RadioBrowserSearch(context, this)
// radioBrowserSearch.searchStation(context, station.radioBrowserStationUuid, Keys.SEARCH_TYPE_BY_UUID)
// } else if (station.remoteStationLocation.isNotEmpty()) {
// // download playlist // todo check content type detection is necessary here
// DownloadHelper.downloadPlaylists(context, arrayOf(station.remoteStationLocation))
// } else {
// Log.w(TAG, "Unable to update station: ${station.name}.")
// }
// }
/* Determines if position is last */ /* Determines if position is last */
private fun isPositionFooter(position: Int): Boolean { private fun isPositionFooter(position: Int): Boolean {
return position == collection.stations.size return position == collection.stations.size
@@ -600,7 +611,7 @@ class CollectionAdapter(
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
private fun updateRecyclerView(oldCollection: Collection, newCollection: Collection) { private fun updateRecyclerView(oldCollection: Collection, newCollection: Collection) {
collection = newCollection collection = newCollection
if (oldCollection.stations.size == 0 && newCollection.stations.size > 0) { if (oldCollection.stations.isEmpty() && newCollection.stations.isNotEmpty()) {
// data set has been initialized - redraw the whole list // data set has been initialized - redraw the whole list
notifyDataSetChanged() notifyDataSetChanged()
} else { } else {
@@ -672,6 +683,8 @@ class CollectionAdapter(
val stationImageView: ImageView = stationCardLayout.findViewById(R.id.station_icon) val stationImageView: ImageView = stationCardLayout.findViewById(R.id.station_icon)
val stationNameView: TextView = stationCardLayout.findViewById(R.id.station_name) val stationNameView: TextView = stationCardLayout.findViewById(R.id.station_name)
val stationStarredView: ImageView = stationCardLayout.findViewById(R.id.starred_icon) val stationStarredView: ImageView = stationCardLayout.findViewById(R.id.starred_icon)
val bufferingProgress: ProgressBar = stationCardLayout.findViewById(R.id.buffering_progress)
val downloadProgress: ProgressBar = stationCardLayout.findViewById(R.id.download_progress)
// val menuButtonView: ImageView = stationCardLayout.findViewById(R.id.menu_button) // val menuButtonView: ImageView = stationCardLayout.findViewById(R.id.menu_button)
val playButtonView: ImageView = stationCardLayout.findViewById(R.id.playback_button) val playButtonView: ImageView = stationCardLayout.findViewById(R.id.playback_button)

View File

@@ -48,7 +48,9 @@ data class Station(
@Expose var radioBrowserStationUuid: String = String(), @Expose var radioBrowserStationUuid: String = String(),
@Expose var radioBrowserChangeUuid: String = String(), @Expose var radioBrowserChangeUuid: String = String(),
@Expose var bitrate: Int = 0, @Expose var bitrate: Int = 0,
@Expose var codec: String = String() @Expose var codec: String = String(),
@Expose var bufferingProgress: Int = 0,
@Expose var downloadProgress: Int = 0
) : Parcelable { ) : Parcelable {
@@ -79,6 +81,12 @@ data class Station(
} }
/* Getter for media type */
fun getMediaType(): String {
return streamContent
}
/* Creates a deep copy of a Station */ /* Creates a deep copy of a Station */
fun deepCopy(): Station { fun deepCopy(): Station {
return Station( return Station(
@@ -101,7 +109,9 @@ data class Station(
radioBrowserStationUuid = radioBrowserStationUuid, radioBrowserStationUuid = radioBrowserStationUuid,
radioBrowserChangeUuid = radioBrowserChangeUuid, radioBrowserChangeUuid = radioBrowserChangeUuid,
bitrate = bitrate, bitrate = bitrate,
codec = codec codec = codec,
bufferingProgress = bufferingProgress,
downloadProgress = downloadProgress
) )
} }
} }

View File

@@ -20,6 +20,8 @@ import androidx.media3.common.Metadata
import androidx.media3.common.util.UnstableApi import androidx.media3.common.util.UnstableApi
import androidx.media3.extractor.metadata.icy.IcyHeaders import androidx.media3.extractor.metadata.icy.IcyHeaders
import androidx.media3.extractor.metadata.icy.IcyInfo import androidx.media3.extractor.metadata.icy.IcyInfo
import androidx.media3.extractor.metadata.id3.Id3Frame
import androidx.media3.extractor.metadata.id3.TextInformationFrame
import com.michatec.radio.Keys import com.michatec.radio.Keys
import kotlin.math.min import kotlin.math.min
@@ -37,28 +39,51 @@ object AudioHelper {
/* Extract audio stream metadata */ /* Extract audio stream metadata */
@OptIn(UnstableApi::class) @OptIn(UnstableApi::class)
fun getMetadataString(metadata: Metadata): String { fun getMetadataString(metadata: Metadata): String {
var metadataString = String() var title = ""
var artist = ""
var album = ""
for (i in 0 until metadata.length()) { for (i in 0 until metadata.length()) {
// extract IceCast metadata // extract IceCast metadata
when (val entry = metadata.get(i)) { when (val entry = metadata.get(i)) {
is IcyInfo -> { is IcyInfo -> {
metadataString = entry.title.toString() title = entry.title.toString()
} }
is IcyHeaders -> { is IcyHeaders -> {
Log.i(TAG, "icyHeaders:" + entry.name + " - " + entry.genre) Log.i(TAG, "icyHeaders:" + entry.name + " - " + entry.genre)
} }
is Id3Frame -> {
when (entry) {
is TextInformationFrame -> {
when (entry.id) {
"TIT2" -> title = entry.values.getOrNull(0) ?: "" // Title
"TPE1" -> artist = entry.values.getOrNull(0) ?: "" // Artist
"TALB" -> album = entry.values.getOrNull(0) ?: "" // Album
}
}
else -> {
Log.d(TAG, "Unhandled ID3 frame: ${entry.javaClass.simpleName}")
}
}
}
else -> { else -> {
Log.w(TAG, "Unsupported metadata received (type = ${entry.javaClass.simpleName})") Log.w(TAG, "Unsupported metadata received (type = ${entry.javaClass.simpleName})")
} }
} }
// TODO implement HLS metadata extraction (Id3Frame / PrivFrame) }
// https://exoplayer.dev/doc/reference/com/google/android/exoplayer2/metadata/Metadata.Entry.html // Build metadata string
var metadataString = title
if (artist.isNotEmpty() && title.isNotEmpty()) {
metadataString = "$artist - $title"
}
if (album.isNotEmpty() && metadataString.isNotEmpty()) {
metadataString += " ($album)"
} }
// ensure a max length of the metadata string // ensure a max length of the metadata string
if (metadataString.isNotEmpty()) { if (metadataString.isNotEmpty()) {
metadataString = metadataString.substring(0, min(metadataString.length, Keys.DEFAULT_MAX_LENGTH_OF_METADATA_ENTRY)) metadataString = metadataString.take(min(metadataString.length, Keys.DEFAULT_MAX_LENGTH_OF_METADATA_ENTRY))
} }
return metadataString return metadataString
} }

View File

@@ -688,7 +688,6 @@ object CollectionHelper {
/* Creates a MediaItem with MediaMetadata for a single radio station - used to prepare player */ /* Creates a MediaItem with MediaMetadata for a single radio station - used to prepare player */
fun buildMediaItem(context: Context, station: Station): MediaItem { fun buildMediaItem(context: Context, station: Station): MediaItem {
// todo implement HLS MediaItems
// put uri in RequestMetadata - credit: https://stackoverflow.com/a/70103460 // put uri in RequestMetadata - credit: https://stackoverflow.com/a/70103460
val requestMetadata = MediaItem.RequestMetadata.Builder().apply { val requestMetadata = MediaItem.RequestMetadata.Builder().apply {
setMediaUri(station.getStreamUri().toUri()) setMediaUri(station.getStreamUri().toUri())
@@ -713,7 +712,7 @@ object CollectionHelper {
setMediaId(station.uuid) setMediaId(station.uuid)
setRequestMetadata(requestMetadata) setRequestMetadata(requestMetadata)
setMediaMetadata(mediaMetadata) setMediaMetadata(mediaMetadata)
//setMimeType(station.getMediaType()) setMimeType(station.getMediaType())
setUri(station.getStreamUri().toUri()) setUri(station.getStreamUri().toUri())
}.build() }.build()
} }

View File

@@ -417,16 +417,14 @@ object FileHelper {
/* Reads InputStream from file uri and returns it as String */ /* Reads InputStream from file uri and returns it as String */
private fun readTextFileFromFile(context: Context): String { private fun readTextFileFromFile(context: Context): String {
// todo read https://commonsware.com/blog/2016/03/15/how-consume-content-uri.html
// https://developer.android.com/training/secure-file-sharing/retrieve-info
// check if file exists // check if file exists
val file = File(context.getExternalFilesDir(Keys.FOLDER_COLLECTION), Keys.COLLECTION_FILE) val file = File(context.getExternalFilesDir(Keys.FOLDER_COLLECTION), Keys.COLLECTION_FILE)
if (!file.exists() || !file.canRead()) { if (!file.exists() || !file.canRead()) {
return String() return String()
} }
// read until last line reached // read until last line reached
val stream: InputStream = file.inputStream() val uri = Uri.fromFile(file)
val stream: InputStream = context.contentResolver.openInputStream(uri) ?: return String()
val reader = BufferedReader(InputStreamReader(stream)) val reader = BufferedReader(InputStreamReader(stream))
val builder: StringBuilder = StringBuilder() val builder: StringBuilder = StringBuilder()
reader.forEachLine { reader.forEachLine {

View File

@@ -14,6 +14,7 @@
package com.michatec.radio.ui package com.michatec.radio.ui
import android.annotation.SuppressLint
import android.content.ClipData import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.Context import android.content.Context
@@ -131,6 +132,7 @@ data class LayoutHolder(var rootView: View) {
/* Updates the player views */ /* Updates the player views */
@SuppressLint("DefaultLocale")
fun updatePlayerViews(context: Context, station: Station, isPlaying: Boolean) { fun updatePlayerViews(context: Context, station: Station, isPlaying: Boolean) {
// set default metadata views, when playback has stopped // set default metadata views, when playback has stopped
@@ -164,12 +166,20 @@ data class LayoutHolder(var rootView: View) {
// show only the codec when the bitrate is at "0" from radio-browser.info API // show only the codec when the bitrate is at "0" from radio-browser.info API
station.codec station.codec
} else { } else {
val kiloBytesPerSecond = station.bitrate / 8F
val dataRateString = if (kiloBytesPerSecond >= 1000) {
String.format("%.2f mb/s", kiloBytesPerSecond / 1000F)
} else {
String.format("%.0f kb/s", kiloBytesPerSecond)
}
// show the bitrate and codec if the result is available in the radio-browser.info API // show the bitrate and codec if the result is available in the radio-browser.info API
buildString { buildString {
append(station.codec) append(station.codec)
append(" | ") append(" | ")
append(station.bitrate) append(station.bitrate)
append("kbps") append("kbps")
append(" | ")
append(dataRateString)
} }
} }
} else { } else {

View File

@@ -31,6 +31,21 @@
app:shapeAppearanceOverlay="@style/RoundedCorners" app:shapeAppearanceOverlay="@style/RoundedCorners"
app:srcCompat="@drawable/ic_default_station_image_72dp" /> app:srcCompat="@drawable/ic_default_station_image_72dp" />
<ProgressBar
android:id="@+id/download_progress"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="0dp"
android:layout_height="4dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginTop="8dp"
android:progressTint="@color/player_button_background"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="@+id/station_icon"
app:layout_constraintStart_toStartOf="@+id/station_icon"
app:layout_constraintTop_toTopOf="@+id/station_icon"
tools:visibility="visible" />
<com.google.android.material.imageview.ShapeableImageView <com.google.android.material.imageview.ShapeableImageView
android:id="@+id/change_image_view" android:id="@+id/change_image_view"
android:layout_width="0dp" android:layout_width="0dp"
@@ -155,6 +170,21 @@
app:layout_constraintEnd_toStartOf="@+id/save_button" app:layout_constraintEnd_toStartOf="@+id/save_button"
app:layout_constraintTop_toTopOf="@+id/save_button" /> app:layout_constraintTop_toTopOf="@+id/save_button" />
<ProgressBar
android:id="@+id/buffering_progress"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="0dp"
android:layout_height="4dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="4dp"
android:progressTint="@color/player_button_background"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:visibility="visible" />
<androidx.constraintlayout.widget.Group <androidx.constraintlayout.widget.Group
android:id="@+id/default_edit_views" android:id="@+id/default_edit_views"
android:layout_width="0dp" android:layout_width="0dp"

View File

@@ -2,7 +2,7 @@
<paths> <paths>
<external-files-path <external-files-path
name="my_images" name="my_images"
path="Android/data/com.michatec.urlradio/files/Pictures" /> path="Android/data/com.michatec.radio/files/Pictures" />
<external-path <external-path
name="external_files" name="external_files"
path="." /> path="." />

View File

@@ -10,7 +10,7 @@
android:shortcutShortLabel="@string/shortcut_last_station_short_label"> android:shortcutShortLabel="@string/shortcut_last_station_short_label">
<intent <intent
android:action="com.jamal2367.urlradio.action.START" android:action="com.michatec.radio.action.START"
android:targetClass="com.michatec.radio.MainActivity" android:targetClass="com.michatec.radio.MainActivity"
android:targetPackage="com.michatec.radio"> android:targetPackage="com.michatec.radio">
<extra <extra

View File

@@ -3,7 +3,7 @@
plugins { plugins {
id 'com.android.application' version '9.0.0' apply false id 'com.android.application' version '9.0.0' apply false
id 'com.android.library' version '9.0.0' apply false id 'com.android.library' version '9.0.0' apply false
id 'org.jetbrains.kotlin.android' version "2.3.0" apply false id 'org.jetbrains.kotlin.android' version "2.3.10" apply false
} }
tasks.register('clean', Delete) { tasks.register('clean', Delete) {

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME