mirror of
https://github.com/Michatec/Radio.git
synced 2026-03-31 23:46:28 +02:00
Compare commits
49 Commits
14.1
...
2d2d95875f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d2d95875f | ||
|
|
46e7b905c8 | ||
|
|
d033ae6344 | ||
|
|
fc275d349b | ||
|
|
4e6f7c7c67 | ||
|
|
272d6fd908 | ||
|
|
4a30828c99 | ||
|
|
963f6e7618 | ||
|
|
29ead7e1d8 | ||
| 9140b54a23 | |||
| 57c4075f19 | |||
| 5334b88f1d | |||
| 5978aab0aa | |||
|
|
c3c2ccfdd9 | ||
|
|
38fb4d162b | ||
|
|
3c3c18104b | ||
|
|
4e1ee7d6a7 | ||
|
|
64ba020eae | ||
|
|
b35d7bd67c | ||
|
|
d3dd098639 | ||
|
|
a8f6c7f946 | ||
|
|
765ccc9250 | ||
|
|
c9d6cf27ec | ||
|
|
7b7579d416 | ||
|
|
d452d7f7ee | ||
|
|
2bb3d22cab | ||
|
|
b4ed3e107c | ||
|
|
ef843b601a | ||
|
|
219d54f4e4 | ||
|
|
c8a39bf2d7 | ||
|
|
81ff920c2c | ||
|
|
f4a5209e14 | ||
|
|
c7b7bdcbed | ||
|
|
eae9176f21 | ||
|
|
b3a833fa44 | ||
|
|
35a8ed46ff | ||
|
|
fd18943878 | ||
|
|
094fb508e2 | ||
|
|
874cf1eb39 | ||
|
|
211cb387ad | ||
|
|
5481a06343 | ||
| fbafcb9773 | |||
| 27c405fd58 | |||
|
|
c2cba26c66 | ||
|
|
9224e45b18 | ||
|
|
90164d7e6a | ||
|
|
45e208e7a9 | ||
|
|
1d040e3edc | ||
|
|
efe17bc82b |
10
.github/workflows/gradle-publish.yml
vendored
10
.github/workflows/gradle-publish.yml
vendored
@@ -3,8 +3,10 @@ name: Build and publish APK
|
|||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
tags:
|
branches: [ "main" ]
|
||||||
- 'b*'
|
pull_request:
|
||||||
|
branches: [ "main" ]
|
||||||
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
ANDROID_HOME: /usr/local/lib/android/sdk/
|
ANDROID_HOME: /usr/local/lib/android/sdk/
|
||||||
@@ -38,7 +40,7 @@ jobs:
|
|||||||
## It is not necessary to check for cache hit as it
|
## It is not necessary to check for cache hit as it
|
||||||
## will not download Android SDK again
|
## will not download Android SDK again
|
||||||
#if: steps.cache-android-sdk.outputs.cache-hit != 'true'
|
#if: steps.cache-android-sdk.outputs.cache-hit != 'true'
|
||||||
uses: android-actions/setup-android@v3
|
uses: android-actions/setup-android@v4
|
||||||
with:
|
with:
|
||||||
packages: ''
|
packages: ''
|
||||||
|
|
||||||
@@ -70,4 +72,4 @@ jobs:
|
|||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: app-release
|
name: app-release
|
||||||
path: app-release.apk
|
path: app-release.apk
|
||||||
|
|||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -4,4 +4,6 @@
|
|||||||
/local.properties
|
/local.properties
|
||||||
/.idea
|
/.idea
|
||||||
/build
|
/build
|
||||||
/captures
|
/captures
|
||||||
|
/gradle/gradle-daemon-jvm.properties
|
||||||
|
/.kotlin
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
Copyright (c) 2025 - Michatec
|
Copyright (c) 2026 - Michatec
|
||||||
--------------------------------
|
--------------------------------
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
|||||||
13
README.md
13
README.md
@@ -10,6 +10,19 @@
|
|||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>❗Warning</summary>
|
||||||
|
<br>
|
||||||
|
<p>
|
||||||
|
Google has announced that, starting in 2026/2027, all apps on certified Android devices will require the developer to submit personal identity details directly to Google.
|
||||||
|
|
||||||
|
Since the developers of this app do not agree to this requirement, this app will no longer work on certified Android devices after that time.
|
||||||
|
</p>
|
||||||
|
<a href="https://github.com/woheller69/FreeDroidWarn">ℹ️ More Information</a>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>⚙️ Install Radio</summary>
|
<summary>⚙️ Install Radio</summary>
|
||||||
<br>
|
<br>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'com.android.application'
|
alias libs.plugins.android.application
|
||||||
id 'kotlin-parcelize'
|
id 'kotlin-parcelize'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,8 +19,8 @@ android {
|
|||||||
applicationId 'com.michatec.radio'
|
applicationId 'com.michatec.radio'
|
||||||
minSdk 28
|
minSdk 28
|
||||||
targetSdk 36
|
targetSdk 36
|
||||||
versionCode 141
|
versionCode 143
|
||||||
versionName '14.1'
|
versionName '14.3'
|
||||||
resourceConfigurations += ['en', 'de', 'el', 'nl', 'pl', 'ru','uk', 'ja', 'da', 'fr']
|
resourceConfigurations += ['en', 'de', 'el', 'nl', 'pl', 'ru','uk', 'ja', 'da', 'fr']
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,24 +55,26 @@ dependencies {
|
|||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
|
|
||||||
// Google Stuff //
|
// Google Stuff //
|
||||||
implementation 'com.google.android.material:material:1.13.0'
|
implementation libs.material
|
||||||
implementation 'com.google.code.gson:gson:2.13.2'
|
implementation libs.gson
|
||||||
|
|
||||||
// AndroidX Stuff //
|
// AndroidX Stuff //
|
||||||
implementation 'androidx.core:core-ktx:1.17.0'
|
implementation libs.core.ktx
|
||||||
implementation 'androidx.activity:activity-ktx:1.12.4'
|
implementation libs.activity.ktx
|
||||||
implementation 'androidx.palette:palette-ktx:1.0.0'
|
implementation libs.palette.ktx
|
||||||
implementation 'androidx.preference:preference-ktx:1.2.1'
|
implementation libs.preference.ktx
|
||||||
implementation 'androidx.media:media:1.7.1'
|
implementation libs.media
|
||||||
implementation 'androidx.media3:media3-exoplayer:1.9.2'
|
implementation libs.media3.exoplayer
|
||||||
implementation 'androidx.media3:media3-exoplayer-hls:1.9.2'
|
implementation libs.media3.exoplayer.hls
|
||||||
implementation 'androidx.media3:media3-session:1.9.2'
|
implementation libs.media3.session
|
||||||
implementation 'androidx.media3:media3-datasource-okhttp:1.9.2'
|
implementation libs.media3.datasource.okhttp
|
||||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.9.7'
|
implementation libs.navigation.fragment.ktx
|
||||||
implementation 'androidx.navigation:navigation-ui-ktx:2.9.7'
|
implementation libs.navigation.ui.ktx
|
||||||
implementation 'androidx.work:work-runtime-ktx:2.11.1'
|
implementation libs.work.runtime.ktx
|
||||||
|
|
||||||
|
implementation libs.freedroidwarn
|
||||||
|
|
||||||
// Volley HTTP request //
|
// Volley HTTP request //
|
||||||
implementation 'com.android.volley:volley:1.2.1'
|
implementation libs.volley
|
||||||
implementation 'androidx.compose.material3:material3:1.4.0'
|
implementation libs.material3
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme"
|
android:theme="@style/AppTheme"
|
||||||
android:usesCleartextTraffic="true"
|
android:usesCleartextTraffic="true"
|
||||||
tools:targetApi="tiramisu">
|
tools:targetApi="33">
|
||||||
|
|
||||||
<!-- ANDROID AUTO SUPPORT -->
|
<!-- ANDROID AUTO SUPPORT -->
|
||||||
<!-- https://developer.android.com/training/auto/audio/ -->
|
<!-- https://developer.android.com/training/auto/audio/ -->
|
||||||
@@ -58,6 +58,7 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<!-- react to playlist-links based on file extension -->
|
<!-- react to playlist-links based on file extension -->
|
||||||
|
<!-- This is intended as an App Link for specific extensions -->
|
||||||
<intent-filter android:autoVerify="true">
|
<intent-filter android:autoVerify="true">
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
@@ -66,14 +67,16 @@
|
|||||||
|
|
||||||
<data android:scheme="http" />
|
<data android:scheme="http" />
|
||||||
<data android:scheme="https" />
|
<data android:scheme="https" />
|
||||||
<data android:host="*" />
|
<data android:host="*"
|
||||||
|
tools:ignore="AppLinkUrlError" />
|
||||||
<data android:pathPattern=".*\\.m3u" />
|
<data android:pathPattern=".*\\.m3u" />
|
||||||
<data android:pathPattern=".*\\.m3u8" />
|
<data android:pathPattern=".*\\.m3u8" />
|
||||||
<data android:pathPattern=".*\\.pls" />
|
<data android:pathPattern=".*\\.pls" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<!-- react to playlist-links based on mimetype -->
|
<!-- react to playlist-links based on mimetype -->
|
||||||
<intent-filter android:autoVerify="true">
|
<!-- Note: MIME types prevent strict App Link verification, but are kept as requested -->
|
||||||
|
<intent-filter android:autoVerify="false">
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|||||||
@@ -78,7 +78,6 @@ object Keys {
|
|||||||
// default const values
|
// default const values
|
||||||
const val DEFAULT_SIZE_OF_METADATA_HISTORY: Int = 25
|
const val DEFAULT_SIZE_OF_METADATA_HISTORY: Int = 25
|
||||||
const val DEFAULT_MAX_LENGTH_OF_METADATA_ENTRY: Int = 127
|
const val DEFAULT_MAX_LENGTH_OF_METADATA_ENTRY: Int = 127
|
||||||
const val DEFAULT_DOWNLOAD_OVER_MOBILE: Boolean = false
|
|
||||||
const val ACTIVE_DOWNLOADS_EMPTY: String = "zero"
|
const val ACTIVE_DOWNLOADS_EMPTY: String = "zero"
|
||||||
const val DEFAULT_MAX_RECONNECTION_COUNT: Int = 30
|
const val DEFAULT_MAX_RECONNECTION_COUNT: Int = 30
|
||||||
const val LARGE_BUFFER_SIZE_MULTIPLIER: Int = 8
|
const val LARGE_BUFFER_SIZE_MULTIPLIER: Int = 8
|
||||||
@@ -149,9 +148,6 @@ object Keys {
|
|||||||
const val RADIO_BROWSER_API_BASE: String = "all.api.radio-browser.info"
|
const val RADIO_BROWSER_API_BASE: String = "all.api.radio-browser.info"
|
||||||
const val RADIO_BROWSER_API_DEFAULT: String = "de1.api.radio-browser.info"
|
const val RADIO_BROWSER_API_DEFAULT: String = "de1.api.radio-browser.info"
|
||||||
|
|
||||||
// locations
|
|
||||||
const val LOCATION_DEFAULT_STATION_IMAGE: String = "android.resource://com.michatec.radio/drawable/ic_default_station_image_24dp"
|
|
||||||
|
|
||||||
// sizes (in dp)
|
// sizes (in dp)
|
||||||
const val SIZE_STATION_IMAGE_CARD: Int = 72
|
const val SIZE_STATION_IMAGE_CARD: Int = 72
|
||||||
const val SIZE_STATION_IMAGE_MAXIMUM: Int = 640
|
const val SIZE_STATION_IMAGE_MAXIMUM: Int = 640
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ 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.PreferencesHelper
|
import com.michatec.radio.helpers.PreferencesHelper
|
||||||
|
import org.woheller69.freeDroidWarn.FreeDroidWarn
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MainActivity class
|
* MainActivity class
|
||||||
@@ -39,6 +39,9 @@ class MainActivity : AppCompatActivity() {
|
|||||||
/* Overrides onCreate from AppCompatActivity */
|
/* Overrides onCreate from AppCompatActivity */
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
// Free Android
|
||||||
|
FreeDroidWarn.showWarningOnUpgrade(this, BuildConfig.VERSION_CODE)
|
||||||
|
|
||||||
// set up views
|
// set up views
|
||||||
setContentView(R.layout.activity_main)
|
setContentView(R.layout.activity_main)
|
||||||
@@ -60,6 +63,16 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Overrides onResume from AppCompatActivity */
|
||||||
|
override fun onResume() {
|
||||||
|
try {
|
||||||
|
super.onResume()
|
||||||
|
} catch (_: ClassCastException) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Overrides onSupportNavigateUp from AppCompatActivity */
|
/* Overrides onSupportNavigateUp from AppCompatActivity */
|
||||||
override fun onSupportNavigateUp(): Boolean {
|
override fun onSupportNavigateUp(): Boolean {
|
||||||
// Taken from: https://developer.android.com/guide/navigation/navigation-ui#action_bar
|
// Taken from: https://developer.android.com/guide/navigation/navigation-ui#action_bar
|
||||||
|
|||||||
@@ -400,19 +400,19 @@ class PlayerService : MediaLibraryService() {
|
|||||||
customLayout: ImmutableList<CommandButton>,
|
customLayout: ImmutableList<CommandButton>,
|
||||||
showPauseButton: Boolean
|
showPauseButton: Boolean
|
||||||
): ImmutableList<CommandButton> {
|
): ImmutableList<CommandButton> {
|
||||||
val seekToPreviousCommandButton = CommandButton.Builder()
|
val seekToPreviousCommandButton = CommandButton.Builder(CommandButton.ICON_UNDEFINED)
|
||||||
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
|
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
|
||||||
.setIconResId(R.drawable.ic_notification_skip_to_previous_36dp)
|
.setCustomIconResId(R.drawable.ic_notification_skip_to_previous_36dp)
|
||||||
.setEnabled(true)
|
.setEnabled(true)
|
||||||
.build()
|
.build()
|
||||||
val playCommandButton = CommandButton.Builder()
|
val playCommandButton = CommandButton.Builder(CommandButton.ICON_UNDEFINED)
|
||||||
.setPlayerCommand(Player.COMMAND_PLAY_PAUSE)
|
.setPlayerCommand(Player.COMMAND_PLAY_PAUSE)
|
||||||
.setIconResId(if (player.isPlaying) R.drawable.ic_notification_stop_36dp else R.drawable.ic_notification_play_36dp)
|
.setCustomIconResId(if (player.isPlaying) R.drawable.ic_notification_stop_36dp else R.drawable.ic_notification_play_36dp)
|
||||||
.setEnabled(true)
|
.setEnabled(true)
|
||||||
.build()
|
.build()
|
||||||
val seekToNextCommandButton = CommandButton.Builder()
|
val seekToNextCommandButton = CommandButton.Builder(CommandButton.ICON_UNDEFINED)
|
||||||
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
|
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
|
||||||
.setIconResId(R.drawable.ic_notification_skip_to_next_36dp)
|
.setCustomIconResId(R.drawable.ic_notification_skip_to_next_36dp)
|
||||||
.setEnabled(true)
|
.setEnabled(true)
|
||||||
.build()
|
.build()
|
||||||
val commandButtons: MutableList<CommandButton> = mutableListOf(
|
val commandButtons: MutableList<CommandButton> = mutableListOf(
|
||||||
|
|||||||
@@ -34,9 +34,4 @@ class Radio : Application() {
|
|||||||
AppThemeHelper.setTheme(PreferencesHelper.loadThemeSelection())
|
AppThemeHelper.setTheme(PreferencesHelper.loadThemeSelection())
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Implements onTerminate */
|
|
||||||
override fun onTerminate() {
|
|
||||||
super.onTerminate()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import androidx.activity.result.ActivityResult
|
|||||||
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
|
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.core.os.bundleOf
|
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.preference.*
|
import androidx.preference.*
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
@@ -427,9 +426,9 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
|||||||
val sourceUri: Uri? = result.data?.data
|
val sourceUri: Uri? = result.data?.data
|
||||||
if (sourceUri != null) {
|
if (sourceUri != null) {
|
||||||
// open and import OPML in player fragment
|
// open and import OPML in player fragment
|
||||||
val bundle: Bundle = bundleOf(
|
val bundle = Bundle().apply {
|
||||||
Keys.ARG_RESTORE_COLLECTION to "$sourceUri"
|
putString(Keys.ARG_RESTORE_COLLECTION, "$sourceUri")
|
||||||
)
|
}
|
||||||
this.findNavController().navigate(R.id.player_destination, bundle)
|
this.findNavController().navigate(R.id.player_destination, bundle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -445,7 +444,9 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
|||||||
Snackbar.LENGTH_LONG
|
Snackbar.LENGTH_LONG
|
||||||
).show()
|
).show()
|
||||||
// update collection in player screen
|
// update collection in player screen
|
||||||
val bundle: Bundle = bundleOf(Keys.ARG_UPDATE_COLLECTION to true)
|
val bundle = Bundle().apply {
|
||||||
|
putBoolean(Keys.ARG_UPDATE_COLLECTION, true)
|
||||||
|
}
|
||||||
this.findNavController().navigate(R.id.player_destination, bundle)
|
this.findNavController().navigate(R.id.player_destination, bundle)
|
||||||
} else {
|
} else {
|
||||||
ErrorDialog().show(
|
ErrorDialog().show(
|
||||||
@@ -466,9 +467,9 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
|
|||||||
Snackbar.LENGTH_LONG
|
Snackbar.LENGTH_LONG
|
||||||
).show()
|
).show()
|
||||||
// update collection in player screen
|
// update collection in player screen
|
||||||
val bundle: Bundle = bundleOf(
|
val bundle = Bundle().apply {
|
||||||
Keys.ARG_UPDATE_IMAGES to true
|
putBoolean(Keys.ARG_UPDATE_IMAGES, true)
|
||||||
)
|
}
|
||||||
this.findNavController().navigate(R.id.player_destination, bundle)
|
this.findNavController().navigate(R.id.player_destination, bundle)
|
||||||
} else {
|
} else {
|
||||||
ErrorDialog().show(
|
ErrorDialog().show(
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ package com.michatec.radio.extensions
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.core.os.bundleOf
|
|
||||||
import androidx.media3.session.MediaController
|
import androidx.media3.session.MediaController
|
||||||
import androidx.media3.session.SessionCommand
|
import androidx.media3.session.SessionCommand
|
||||||
import androidx.media3.session.SessionResult
|
import androidx.media3.session.SessionResult
|
||||||
@@ -71,8 +70,11 @@ fun MediaController.play(context: Context, station: Station) {
|
|||||||
|
|
||||||
/* Starts playback with of a stream url */
|
/* Starts playback with of a stream url */
|
||||||
fun MediaController.playStreamDirectly(streamUri: String) {
|
fun MediaController.playStreamDirectly(streamUri: String) {
|
||||||
|
val bundle = Bundle().apply {
|
||||||
|
putString(Keys.KEY_STREAM_URI, streamUri)
|
||||||
|
}
|
||||||
sendCustomCommand(
|
sendCustomCommand(
|
||||||
SessionCommand(Keys.CMD_PLAY_STREAM, Bundle.EMPTY),
|
SessionCommand(Keys.CMD_PLAY_STREAM, Bundle.EMPTY),
|
||||||
bundleOf(Pair(Keys.KEY_STREAM_URI, streamUri))
|
bundle
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -33,7 +33,6 @@ import kotlinx.coroutines.Dispatchers.IO
|
|||||||
import java.io.*
|
import java.io.*
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
import kotlin.coroutines.suspendCoroutine
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -292,7 +291,7 @@ object FileHelper {
|
|||||||
collection: Collection,
|
collection: Collection,
|
||||||
lastUpdate: Date
|
lastUpdate: Date
|
||||||
) {
|
) {
|
||||||
return suspendCoroutine { cont ->
|
return suspendCancellableCoroutine { cont ->
|
||||||
cont.resume(saveCollection(context, collection, lastUpdate))
|
cont.resume(saveCollection(context, collection, lastUpdate))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -311,7 +310,7 @@ object FileHelper {
|
|||||||
originalFileUri: Uri,
|
originalFileUri: Uri,
|
||||||
targetFileUri: Uri
|
targetFileUri: Uri
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return suspendCoroutine { cont ->
|
return suspendCancellableCoroutine { cont ->
|
||||||
cont.resume(copyFile(context, originalFileUri, targetFileUri))
|
cont.resume(copyFile(context, originalFileUri, targetFileUri))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -319,7 +318,7 @@ object FileHelper {
|
|||||||
|
|
||||||
/* Suspend function: Exports collection of stations as M3U file - local backup copy */
|
/* Suspend function: Exports collection of stations as M3U file - local backup copy */
|
||||||
suspend fun backupCollectionAsM3uSuspended(context: Context, collection: Collection) {
|
suspend fun backupCollectionAsM3uSuspended(context: Context, collection: Collection) {
|
||||||
return suspendCoroutine { cont ->
|
return suspendCancellableCoroutine { cont ->
|
||||||
Log.v(TAG, "Backing up collection as M3U - Thread: ${Thread.currentThread().name}")
|
Log.v(TAG, "Backing up collection as M3U - Thread: ${Thread.currentThread().name}")
|
||||||
// create M3U string
|
// create M3U string
|
||||||
val m3uString: String = CollectionHelper.createM3uString(collection)
|
val m3uString: String = CollectionHelper.createM3uString(collection)
|
||||||
@@ -338,7 +337,7 @@ object FileHelper {
|
|||||||
|
|
||||||
/* Suspend function: Exports collection of stations as PLS file - local backup copy */
|
/* Suspend function: Exports collection of stations as PLS file - local backup copy */
|
||||||
suspend fun backupCollectionAsPlsSuspended(context: Context, collection: Collection) {
|
suspend fun backupCollectionAsPlsSuspended(context: Context, collection: Collection) {
|
||||||
return suspendCoroutine { cont ->
|
return suspendCancellableCoroutine { cont ->
|
||||||
Log.v(TAG, "Backing up collection as PLS - Thread: ${Thread.currentThread().name}")
|
Log.v(TAG, "Backing up collection as PLS - Thread: ${Thread.currentThread().name}")
|
||||||
// create PLS string
|
// create PLS string
|
||||||
val plsString: String = CollectionHelper.createPlsString(collection)
|
val plsString: String = CollectionHelper.createPlsString(collection)
|
||||||
|
|||||||
@@ -19,13 +19,13 @@ import android.net.ConnectivityManager
|
|||||||
import android.net.Network
|
import android.net.Network
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.michatec.radio.Keys
|
import com.michatec.radio.Keys
|
||||||
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
import java.net.InetAddress
|
import java.net.InetAddress
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.net.UnknownHostException
|
import java.net.UnknownHostException
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
import kotlin.coroutines.suspendCoroutine
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -105,7 +105,7 @@ object NetworkHelper {
|
|||||||
|
|
||||||
/* Suspend function: Detects content type (mime type) from given URL string - async using coroutine */
|
/* Suspend function: Detects content type (mime type) from given URL string - async using coroutine */
|
||||||
suspend fun detectContentTypeSuspended(urlString: String): ContentType {
|
suspend fun detectContentTypeSuspended(urlString: String): ContentType {
|
||||||
return suspendCoroutine { cont ->
|
return suspendCancellableCoroutine { cont ->
|
||||||
cont.resume(detectContentType(urlString))
|
cont.resume(detectContentType(urlString))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,7 +113,7 @@ object NetworkHelper {
|
|||||||
|
|
||||||
/* Suspend function: Gets a random radio-browser.info api address - async using coroutine */
|
/* Suspend function: Gets a random radio-browser.info api address - async using coroutine */
|
||||||
suspend fun getRadioBrowserServerSuspended(): String {
|
suspend fun getRadioBrowserServerSuspended(): String {
|
||||||
return suspendCoroutine { cont ->
|
return suspendCancellableCoroutine { cont ->
|
||||||
val serverAddress: String = try {
|
val serverAddress: String = try {
|
||||||
// get all available radio browser servers
|
// get all available radio browser servers
|
||||||
val serverAddressList: Array<InetAddress> =
|
val serverAddressList: Array<InetAddress> =
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id 'com.android.application' version '9.0.1' apply false
|
alias libs.plugins.android.application apply false
|
||||||
id 'com.android.library' version '9.0.1' apply false
|
alias libs.plugins.android.library apply false
|
||||||
id 'org.jetbrains.kotlin.android' version "2.3.10" apply false
|
alias libs.plugins.jetbrains.kotlin.android apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register('clean', Delete) {
|
tasks.register('clean', Delete) {
|
||||||
delete rootProject.buildDir()
|
delete rootProject.buildDir()
|
||||||
}
|
}
|
||||||
|
|||||||
42
gradle/libs.versions.toml
Normal file
42
gradle/libs.versions.toml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
[versions]
|
||||||
|
activityKtx = "1.13.0"
|
||||||
|
agp = "9.1.0"
|
||||||
|
coreKtx = "1.18.0"
|
||||||
|
freedroidwarn = "V1.10"
|
||||||
|
gradleToolchainsFoojayResolverConvention = "1.0.0"
|
||||||
|
gson = "2.13.2"
|
||||||
|
kotlin = "2.3.20"
|
||||||
|
material = "1.13.0"
|
||||||
|
material3 = "1.4.0"
|
||||||
|
media = "1.7.1"
|
||||||
|
media3 = "1.10.0"
|
||||||
|
navigation = "2.9.7"
|
||||||
|
paletteKtx = "1.0.0"
|
||||||
|
preferenceKtx = "1.2.1"
|
||||||
|
volley = "1.2.1"
|
||||||
|
workRuntimeKtx = "2.11.2"
|
||||||
|
|
||||||
|
[libraries]
|
||||||
|
activity-ktx = { group = "androidx.activity", name = "activity-ktx", version.ref = "activityKtx" }
|
||||||
|
core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||||
|
freedroidwarn = { group = "com.github.woheller69", name = "FreeDroidWarn", version.ref = "freedroidwarn" }
|
||||||
|
gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" }
|
||||||
|
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
|
||||||
|
material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
|
||||||
|
media = { group = "androidx.media", name = "media", version.ref = "media" }
|
||||||
|
media3-datasource-okhttp = { group = "androidx.media3", name = "media3-datasource-okhttp", version.ref = "media3" }
|
||||||
|
media3-exoplayer = { group = "androidx.media3", name = "media3-exoplayer", version.ref = "media3" }
|
||||||
|
media3-exoplayer-hls = { group = "androidx.media3", name = "media3-exoplayer-hls", version.ref = "media3" }
|
||||||
|
media3-session = { group = "androidx.media3", name = "media3-session", version.ref = "media3" }
|
||||||
|
navigation-fragment-ktx = { group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref = "navigation" }
|
||||||
|
navigation-ui-ktx = { group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "navigation" }
|
||||||
|
palette-ktx = { group = "androidx.palette", name = "palette-ktx", version.ref = "paletteKtx" }
|
||||||
|
preference-ktx = { group = "androidx.preference", name = "preference-ktx", version.ref = "preferenceKtx" }
|
||||||
|
volley = { group = "com.android.volley", name = "volley", version.ref = "volley" }
|
||||||
|
work-runtime-ktx = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "workRuntimeKtx" }
|
||||||
|
|
||||||
|
[plugins]
|
||||||
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
android-library = { id = "com.android.library", version.ref = "agp" }
|
||||||
|
foojay = { id = "org.gradle.toolchains.foojay-resolver-convention", version.ref = "gradleToolchainsFoojayResolverConvention" }
|
||||||
|
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -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.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
2
gradlew
vendored
2
gradlew
vendored
@@ -57,7 +57,7 @@
|
|||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (3) This script is generated from the Groovy template
|
||||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
# https://github.com/gradle/gradle/blob/b631911858264c0b6e4d6603d677ff5218766cee/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
# within the Gradle project.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
|||||||
@@ -3,13 +3,19 @@ pluginManagement {
|
|||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
|
maven { url 'https://jitpack.io' }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0'
|
||||||
|
}
|
||||||
dependencyResolutionManagement {
|
dependencyResolutionManagement {
|
||||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
maven { url 'https://jitpack.io' }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user