73 Commits

Author SHA1 Message Date
Michatec c1bc8b3e19 refactor(android): simplify notification permission request
Remove the explicit permission type from the `RequestPermission`
contract in `MainActivity` to rely on default behavior or
internal contract handling.
2026-05-30 23:11:19 +02:00
Michatec 8835cadd6a fix(ui): specify notification permission type in launcher
Explicitly pass `Manifest.permission.POST_NOTIFICATIONS` to the
`RequestPermission` contract to ensure the correct permission is
requested on Android 13+.
2026-05-30 23:09:50 +02:00
Michatec 384926cf08 chore(build): disable debug mode in build configuration
Set `IS_DEBUG_ENABLED` to `false` in `app/build.gradle.kts` to prepare
the build configuration for release.
2026-05-30 22:51:25 +02:00
Michatec 2cf98b89b3 feat(notification): allow custom intent for notifications
Update `NotificationSys` to support passing a custom `Intent` when
showing notifications. This enables more flexible navigation, such as
opening a web URL for update notifications.

In `PlayerFragment`, use this new capability to trigger a browser
intent for update notifications on non-Android TV devices.
2026-05-30 22:48:26 +02:00
Michatec 6fa1e5e2c0 style(ui): update test notification icon
Replace the white notification icon with a standard 24dp icon in
SettingsFragment to ensure visual consistency.
2026-05-30 22:30:32 +02:00
Michatec 64d0f3a71f fix(android): resolve compilation error in TV feature detection
Update the Android TV feature check to use the instance-based
`packageManager` instead of the static `PackageManager` class.
This ensures the `hasSystemFeature` method is called correctly on
the available context instance.
2026-05-30 22:23:22 +02:00
Michatec c9122d74f8 refactor(android): fix Android TV feature detection syntax
Correct the usage of `hasSystemFeature` by calling it on the `packageManager` instance instead of the `PackageManager` class. This fixes a compilation error in `MainActivity` and `SettingsFragment`.
2026-05-30 22:18:32 +02:00
Michatec 1879827f4b feat(ui): add test notification preference and optimize Android TV logic
Introduce a "Test Notification" preference in the settings menu to allow users to verify the notification system. This preference is automatically hidden on Android TV devices to maintain a clean UI.

Additionally, refactor notification permission handling to skip requests on Android TV and improve the internal check for Leanback support using a lazy property.

Updated string resources for the new preference across all supported languages.
2026-05-30 22:15:51 +02:00
Michatec 23079649c5 style(i18n): fix typography in Danish notification description
Update the apostrophe in `notification_channel_description` to use a
typographic curly apostrophe for better linguistic accuracy in
Danish.
2026-05-30 21:36:22 +02:00
Michatec 2c7e02889d feat(i18n): add notification channel description to all languages
Add the `notification_channel_description` string resource to the
default and all localized string files to provide context for the
notification channel.
2026-05-30 21:33:43 +02:00
Michatec b1bcfa2137 style(i18n): update French typography in string resources
Replace standard apostrophes with typographic curly apostrophes in
`notification_test_content` and `snackbar_failed_permission_notification`
to improve French localization consistency.
2026-05-30 21:23:24 +02:00
Michatec 2814ff2cfa feat(android): implement POST_NOTIFICATIONS permission handling
Add NotificationSys to manage system notifications and update
MainActivity to request POST_NOTIFICATIONS permission on Android 13+.
Includes localized string resources for notification testing and
permission error feedback.
2026-05-30 21:17:01 +02:00
Michachatz a9f8efc72d Update star history chart link in README.md 2026-05-26 00:01:34 +02:00
Michachatz a5e27149ba Add star history section to README
Added a star history section with a chart to the README.
2026-05-26 00:00:35 +02:00
Michatec 2688f22a6a chore(app): add IS_DEBUG_ENABLED build config field
Introduce a `IS_DEBUG_ENABLED` boolean flag in `BuildConfig` for both
debug and release build types. This flag is used to suppress update
notifications in `PlayerFragment` when debugging and before its a version released.
2026-05-15 22:20:23 +02:00
Michachatz 73edf9d816 Merge pull request #73 from Michatec/renovate/gradle-9.x
chore(deps): update gradle to v9.5.1
2026-05-15 21:54:37 +02:00
Michachatz 9f39b1dbbd Merge pull request #74 from Michatec/renovate/media3
fix(deps): update media3 to v1.10.1
2026-05-15 21:54:18 +02:00
Michachatz 5d82d722bd Merge pull request #75 from Michatec/renovate/material
fix(deps): update dependency com.google.android.material:material to v1.14.0
2026-05-15 21:53:50 +02:00
Michatec 5d7df9550e feat(ui): add security preference to settings
Add a new security preference in the settings screen that links to the
SECURITY.md file on GitHub. This includes a new security icon and
localized strings for multiple languages.
2026-05-15 21:51:07 +02:00
renovate[bot] c7b7114010 fix(deps): update dependency com.google.android.material:material to v1.14.0 2026-05-13 18:06:11 +00:00
renovate[bot] ed8f17e436 fix(deps): update media3 to v1.10.1 2026-05-12 16:00:26 +00:00
renovate[bot] 2cc2a23e35 chore(deps): update gradle to v9.5.1 2026-05-12 16:00:20 +00:00
Michachatz f7ddc30127 Merge pull request #72 from Michatec/renovate/freedroidwarn
fix(deps): update dependency com.github.woheller69:freedroidwarn to v1.13
2026-05-11 07:50:32 +02:00
renovate[bot] bc0eb60e04 fix(deps): update dependency com.github.woheller69:freedroidwarn to v1.13 2026-05-10 11:51:08 +00:00
Michatec dcae7bafb5 feat: apply custom theme to ExpandedControllerActivity 2026-05-07 13:49:37 +02:00
Michatec cf3fd0bc56 refactor(media): move MEDIA_BUTTON action to MediaSessionService 2026-05-07 13:43:58 +02:00
Michatec 7d6b0fe136 feat: implement station navigation and update to SDK 37
- Implement `seekToNext` and `seekToPrevious` in `PlayerService` to allow switching between radio stations.
- Update `compileSdk` and `targetSdk` to 37 and bump version to 14.6.
- Modify `PlayerService` to remain active while paused to improve playback resumption.
- Modernize media resumption logic using Media3 `isRecent` check instead of legacy extras.
- Refactor `DefaultRenderersFactory` to use non-nullable `AudioSink` return types.
- General code cleanup using KTX extensions (e.g., `View.isEmpty()`) and Kotlin property access syntax.
2026-05-07 13:40:35 +02:00
Michachatz 0faeea7631 Merge pull request #70 from Michatec/renovate/agp
chore(deps): update agp to v9.2.1
2026-05-07 12:18:23 +02:00
Michachatz c45adf07c8 Merge pull request #71 from Michatec/renovate/media
fix(deps): update dependency androidx.media:media to v1.8.0
2026-05-07 12:18:08 +02:00
renovate[bot] acd1b067b3 fix(deps): update dependency androidx.media:media to v1.8.0 2026-05-06 20:08:16 +00:00
renovate[bot] e3a325f568 chore(deps): update agp to v9.2.1 2026-05-05 17:45:30 +00:00
Michachatz b537df898b Merge pull request #69 from Michatec/renovate/freedroidwarn
fix(deps): update dependency com.github.woheller69:freedroidwarn to v1.12
2026-05-01 13:50:50 +02:00
Michachatz 61675601f3 Merge pull request #68 from Michatec/renovate/gradle-9.x
chore(deps): update gradle to v9.5.0
2026-05-01 13:48:43 +02:00
renovate[bot] 6dc5c47b86 fix(deps): update dependency com.github.woheller69:freedroidwarn to v1.12 2026-04-30 21:46:44 +00:00
renovate[bot] c40ad0cc4a chore(deps): update gradle to v9.5.0 2026-04-28 15:17:44 +00:00
Michachatz 066206c6dc Merge pull request #67 from Michatec/renovate/gson
fix(deps): update dependency com.google.code.gson:gson to v2.14.0
2026-04-26 14:48:57 +02:00
renovate[bot] e8e66c24ef fix(deps): update dependency com.google.code.gson:gson to v2.14.0 2026-04-23 21:10:57 +00:00
Michachatz 56647e7f02 Merge pull request #66 from Michatec/renovate/kotlin-monorepo
chore(deps): update dependency org.jetbrains.kotlin.android to v2.3.21
2026-04-23 17:57:41 +02:00
renovate[bot] a7e0efc913 chore(deps): update dependency org.jetbrains.kotlin.android to v2.3.21 2026-04-23 09:56:51 +00:00
Michachatz 0569bed475 Merge pull request #65 from Michatec/renovate/navigation
fix(deps): update navigation to v2.9.8
2026-04-23 11:52:21 +02:00
renovate[bot] 6e3b187b56 fix(deps): update navigation to v2.9.8 2026-04-22 21:41:22 +00:00
Michachatz c8c0d1783b Merge pull request #64 from Michatec/renovate/agp
chore(deps): update agp to v9.2.0
2026-04-22 06:25:14 +02:00
Michachatz 4974514a81 refactor(ui): Change from normal switch to marquee switches 2026-04-22 06:22:31 +02:00
renovate[bot] afd34c6485 chore(deps): update agp to v9.2.0 2026-04-21 20:38:52 +00:00
Michatec f6208f5e5a style(ui): enable marquee for station metadata and playback controls 2026-04-21 20:04:34 +02:00
Michatec a1beb17b26 style(ui): make language selection dialog scrollable 2026-04-21 19:51:53 +02:00
Michatec 18c28170c5 style(ui): update preference and card icons for presets, equalizer, and visualizer 2026-04-21 19:44:46 +02:00
Michatec 48836334bb refactor(ui): remove unused LanguageSelectionDialog import 2026-04-21 19:26:02 +02:00
Michatec 133c56be4d feat(ui): update and expand translations for multiple languages 2026-04-21 19:15:37 +02:00
Michatec 63d85118a4 feat(ui): add manual language selection to settings 2026-04-21 18:58:53 +02:00
Michatec 4f150221b7 style(ui): add adaptive channel icon 2026-04-20 16:13:28 +02:00
Michatec 5fbf763bd6 style(ui): update banner and banner foreground assets 2026-04-20 14:32:38 +02:00
Michatec 0d3224214e style(ui): update application banner to ic_banner 2026-04-19 12:03:14 +02:00
Michatec 11610084f9 loc(de): add German translations for streaming and loading strings 2026-04-19 11:35:59 +02:00
Michatec 08f303997e refactor(ui): use Locale.ROOT for bitrate formatting 2026-04-19 11:27:54 +02:00
Michatec b60b8cdb7c docs: add SECURITY.md 2026-04-19 00:11:17 +02:00
Michatec b3de68050c refactor(collection): add TODO for notifyDataSetChanged cleanup 2026-04-18 23:35:27 +02:00
Michatec 5d99b2a113 chore: update vscode settings and gradlew reference 2026-04-18 23:29:36 +02:00
Michatec adac340925 style(ui): add tools:visibility to station icon in card layout 2026-04-18 20:23:30 +02:00
Michatec d6037dc0c2 chore(config): prune unsupported locales in locale config 2026-04-18 20:13:04 +02:00
Michatec 297310a784 feat(ui): show snackbar when content is copied to clipboard 2026-04-18 20:08:43 +02:00
Michatec abd9b5ecd9 fix(settings): conditionally enable stream URI editing based on station editing status 2026-04-18 18:39:59 +02:00
Michatec 744a650a91 feat(ui): use checkbox to indicate reordering station status 2026-04-18 17:53:52 +02:00
Michatec 1238a5fba2 style(ui): remove manual card stroke color reset 2026-04-18 16:47:19 +02:00
Michatec cc0b284a7f style(ui): update station card stroke color during reordering and remove play button constraint 2026-04-18 16:31:25 +02:00
Michachatz dfbbd8da33 Fix version formatting in README.md 2026-04-15 21:07:22 +02:00
Michachatz 7690ce5685 Update README to specify Android TV support version 2026-04-15 21:06:42 +02:00
Michachatz 3bbc9280b5 Refactor gradle-publish.yml 2026-04-15 07:18:42 +02:00
Michachatz 0fe32420f2 Merge pull request #61 from Michatec/renovate/agp
chore(deps): update agp to v9.1.1
2026-04-15 06:45:02 +02:00
Michachatz 1491a084f3 Merge pull request #62 from Michatec/renovate/freedroidwarn
fix(deps): update dependency com.github.woheller69:freedroidwarn to v1.11
2026-04-15 06:44:49 +02:00
renovate[bot] 244121edb1 fix(deps): update dependency com.github.woheller69:freedroidwarn to v1.11 2026-04-15 04:39:29 +00:00
renovate[bot] 5479ae25f2 chore(deps): update agp to v9.1.1 2026-04-15 04:39:25 +00:00
Michachatz c0ef50b5a9 Update gradle-publish.yml 2026-04-15 06:38:52 +02:00
62 changed files with 1258 additions and 116 deletions
+3 -3
View File
@@ -12,7 +12,7 @@ permissions:
env:
ANDROID_HOME: /usr/local/lib/android/sdk/
APK_PATH: app/build/outputs/apk/release/app-release.apk
APK_PATH: app/build/outputs/apk/release/app-release-unsigned.apk
APKSIGNER: /usr/local/lib/android/sdk/build-tools/34.0.0/apksigner
ZIPALIGN: /usr/local/lib/android/sdk/build-tools/34.0.0/zipalign
@@ -73,10 +73,10 @@ jobs:
echo "$SIGN_KEY" | base64 -d > key.der
${{ env.APKSIGNER }} sign --key key.der --cert cert.der app-release-aligned.apk
rm cert.der key.der
mv app-release-aligned.apk Radio.${{ env.VERSION_NAME }}-release.apk
mv app-release-aligned.apk Radio_${{ env.VERSION_NAME }}-release.apk
- name: Upload artifact
uses: actions/upload-artifact@v7
with:
name: Radio
path: Radio.${{ env.VERSION_NAME }}-release.apk
path: Radio_${{ env.VERSION_NAME }}-release.apk
+2 -1
View File
@@ -1,3 +1,4 @@
{
"C_Cpp.default.compilerPath": ""
"C_Cpp.default.compilerPath": "",
"java.configuration.updateBuildConfiguration": "interactive"
}
+10 -1
View File
@@ -9,7 +9,7 @@
### ️ About Radio
**Radio is an application with a minimalist approach to listening to radio over the Internet.** <br>
**Radio only offers a very basic search option, and it imports audio streaming links when you tap them in a web browser.** <br>
**Radio now also supports Android TV (Beta) and Cast to Devices (Beta).** <br>
**Radio now also supports Android TV (Beta) and Cast to Devices (Beta) with the Version 14.5 and above.** <br>
**Pull request are welcome at any time.**<br>
**Radio is free software. It is released under the [GPLv3 open source license](https://opensource.org/licenses/GPL-3.0).**
@@ -105,6 +105,15 @@ When **Edit Stations** is enabled:
</details>
----------------------------------------
<details>
<summary>⭐ Star History</summary>
[![Star History Chart](https://api.star-history.com/chart?repos=michatec/radio&type=date&legend=top-left)](https://www.star-history.com/?repos=michatec%2Fradio&type=date&legend=top-left)
</details>
<div align="right">
<table><td>
<a href="#start-of-content">↥ Scroll to top</a>
+59
View File
@@ -0,0 +1,59 @@
# Security Policy
## Reporting a Vulnerability
We take the security of Radio seriously. If you believe you have found a security vulnerability, please report it via **GitHub Issues** before disclosing it publicly.
**Create a new security issue**: [GitHub Issues — Security](https://github.com/michatec/Radio/issues/new?labels=security). We will respond within 48 hours acknowledging your report and work with you to understand and address the issue.
Include in your report:
- Description of the vulnerability
- Steps to reproduce
- Affected versions (if known)
- Potential impact
- Any suggested fixes (optional)
We appreciate responsible disclosure and will credit researchers who report valid security issues (unless you prefer to remain anonymous).
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| 14.5 | :white_check_mark: |
| < 14.5 | :x: |
Only the latest stable release receives security updates. Users on older versions are encouraged to update.
## Security Considerations
### Network Security
Radio streams audio over the internet and connects to the [radio-browser.info](https://www.radio-browser.info/) API for station search. The app allows cleartext HTTP traffic for radio stream compatibility (required for many legacy radio stations).
### Data Collection
- **Station data** (names, images, stream URLs) is fetched from radio-browser.info's public API
- **Station lists** are stored locally on the device only
- **No personal data** is collected or transmitted to michatec servers
- **Usage data** is not collected
### Permissions
Radio requests only the permissions necessary for core functionality:
- `INTERNET` — stream radio and fetch station metadata
- `ACCESS_NETWORK_STATE` — detect connectivity changes
- `FOREGROUND_SERVICE_MEDIA_PLAYBACK` — maintain playback when app is backgrounded
- `WAKE_LOCK` — prevent device from sleeping during playback
### Third-Party Dependencies
Radio uses several third-party libraries. Security issues in dependencies are monitored via Renovate bot for updates. Key dependencies include:
- AndroidX Media3 / ExoPlayer (media playback)
- Google Cast SDK (Chromecast support)
- Volley (HTTP requests)
### File Handling
The app can import M3U/PLS playlist files from external sources. Files are processed locally and stream URLs are validated before playback. Station images are downloaded from radio-browser.info and cached locally.
## Security Update Process
Security patches are delivered via normal app update channels (GitHub Releases, automated update notifications). Critical vulnerabilities may trigger an out-of-band security update.
## Contacts
- General issues: [GitHub Issues](https://github.com/michatec/Radio/issues)
- Project maintainer: [@michatec](https://github.com/michatec)
+6 -4
View File
@@ -5,14 +5,14 @@ plugins {
android {
namespace = "com.michatec.radio"
compileSdk = 36
compileSdk = 37
defaultConfig {
applicationId = "com.michatec.radio"
minSdk = 28
targetSdk = 36
versionCode = 145
versionName = "14.5"
targetSdk = 37
versionCode = 146
versionName = "14.6"
}
compileOptions {
@@ -31,6 +31,7 @@ android {
isCrunchPngs = false
proguardFiles.addAll(listOf(getDefaultProguardFile("proguard-android-optimize.txt"), file("proguard-rules.pro")))
applicationIdSuffix = ".debug"
buildConfigField("boolean", "IS_DEBUG_ENABLED", "true")
}
release {
@@ -38,6 +39,7 @@ android {
isShrinkResources = true
isCrunchPngs = true
proguardFiles.addAll(listOf(getDefaultProguardFile("proguard-android-optimize.txt"), file("proguard-rules.pro")))
buildConfigField("boolean", "IS_DEBUG_ENABLED", "false")
}
}
+6 -10
View File
@@ -9,15 +9,17 @@
android:required="false" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"
tools:ignore="ForegroundServicesPolicy" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
android:name=".Radio"
android:allowBackup="true"
android:banner="@mipmap/ic_launcher"
android:banner="@mipmap/ic_banner"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
@@ -48,6 +50,7 @@
<activity
android:name=".ExpandedControllerActivity"
android:exported="false"
android:theme="@style/CustomCastExpandedControllerStyle"
android:launchMode="singleTask" />
<!-- Main activity for radio station playback on phone and TV -->
@@ -136,6 +139,7 @@
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService" />
<action android:name="android.media.browse.MediaBrowserService" />
<action android:name="android.intent.action.MEDIA_BUTTON" />
<action android:name="com.michatec.radio.action.START_PLAYER_SERVICE" />
</intent-filter>
</service>
@@ -148,14 +152,6 @@
</intent-filter>
</receiver>
<receiver
android:name="androidx.media.session.MediaButtonReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
@@ -120,7 +120,7 @@ class AddStationFragment : Fragment(),
if (searchEditText != null) {
searchEditText.requestFocus()
val imm = requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.showSoftInput(searchEditText, InputMethodManager.SHOW_IMPLICIT)
imm.showSoftInput(searchEditText, 0)
}
}
}
@@ -1,14 +1,40 @@
package com.michatec.radio
import android.content.Context
import android.content.res.Configuration
import android.view.Menu
import com.google.android.gms.cast.framework.CastButtonFactory
import com.google.android.gms.cast.framework.media.widget.ExpandedControllerActivity
import com.michatec.radio.helpers.PreferencesHelper
import java.util.Locale
class ExpandedControllerActivity : ExpandedControllerActivity() {
override fun attachBaseContext(newBase: Context) {
val languageCode = PreferencesHelper.loadSelectedLanguage()
val context = if (languageCode.isEmpty() || languageCode == "system") {
// Use system default locale
newBase
} else {
val locale = Locale.forLanguageTag(languageCode)
val config = Configuration(newBase.resources.configuration)
config.setLocale(locale)
newBase.createConfigurationContext(config)
}
super.attachBaseContext(context)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
super.onCreateOptionsMenu(menu)
menuInflater.inflate(R.menu.expanded_controller, menu)
CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.media_route_menu_item)
return true
}
override fun onResume() {
try {
super.onResume()
} catch (_: ClassCastException) {
// Fix for lifecycle exception on some devices (e.g. Samsung)
}
}
}
@@ -91,6 +91,7 @@ object Keys {
const val PREF_PRESET_REVERB: String = "PRESET_REVERB"
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"
// default const values
const val DEFAULT_SIZE_OF_METADATA_HISTORY: Int = 25
@@ -1,7 +1,11 @@
package com.michatec.radio
import android.Manifest
import android.os.Build
import android.content.Context
import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.os.Bundle
import android.os.Handler
import android.os.Looper
@@ -11,13 +15,17 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.core.view.isVisible
import androidx.navigation.fragment.NavHostFragment
import androidx.activity.result.contract.ActivityResultContracts
import com.google.android.material.snackbar.Snackbar
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.NavigationUI
import androidx.navigation.ui.navigateUp
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 org.woheller69.freeDroidWarn.FreeDroidWarn
import java.util.Locale
/*
* MainActivity class
@@ -27,6 +35,39 @@ class MainActivity : AppCompatActivity() {
/* Main class variables */
private lateinit var appBarConfiguration: AppBarConfiguration
// Check if the device running the app is an Android TV instance
private val isAndroidTV: Boolean by lazy {
packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
}
// request notification permission (for Android 13+)
private val permissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted ->
if (!isGranted) {
Snackbar.make(
findViewById(android.R.id.content),
R.string.snackbar_failed_permission_notification,
Snackbar.LENGTH_LONG
).show()
}
}
/* Overrides attachBaseContext from AppCompatActivity */
override fun attachBaseContext(newBase: Context) {
val languageCode = PreferencesHelper.loadSelectedLanguage()
val context = if (languageCode.isEmpty() || languageCode == "system") {
// Use system default locale
newBase
} else {
val locale = Locale.forLanguageTag(languageCode)
val config = Configuration(newBase.resources.configuration)
config.setLocale(locale)
newBase.createConfigurationContext(config)
}
super.attachBaseContext(context)
}
/* Overrides onCreate from AppCompatActivity */
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
@@ -53,7 +94,7 @@ class MainActivity : AppCompatActivity() {
supportActionBar?.hide()
// TV-specific loading logic: Hide the overlay once the app is ready
if (packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
if (isAndroidTV) {
Handler(Looper.getMainLooper()).postDelayed({
hideLoadingOverlay()
}, 1200)
@@ -63,6 +104,11 @@ class MainActivity : AppCompatActivity() {
// register listener for changes in shared preferences
PreferencesHelper.registerPreferenceChangeListener(sharedPreferenceChangeListener)
// request permissions
if (!isAndroidTV && Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
}
}
/* Hides the loading/splash overlay */
@@ -112,6 +158,9 @@ class MainActivity : AppCompatActivity() {
Keys.PREF_THEME_SELECTION -> {
AppThemeHelper.setTheme(PreferencesHelper.loadThemeSelection())
}
Keys.PREF_LANGUAGE_SELECTED -> {
LanguageHelper.setLanguage(this, PreferencesHelper.loadSelectedLanguage())
}
}
}
/*
@@ -0,0 +1,63 @@
package com.michatec.radio
import androidx.core.app.NotificationCompat
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import com.michatec.radio.R
object NotificationSys {
private const val CHANNEL_ID = "com.michatec.radio.channel"
private const val CHANNEL_NAME = "Notifications"
private const val NOTIFICATION_ID = 1001
fun createNotificationChannel(context: Context) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (notificationManager.getNotificationChannel(CHANNEL_ID) == null) {
val channel = NotificationChannel(
CHANNEL_ID,
CHANNEL_NAME,
NotificationManager.IMPORTANCE_DEFAULT
).apply {
description = context.getString(R.string.notification_channel_description)
}
notificationManager.createNotificationChannel(channel)
}
}
fun showNotification(context: Context, title: String, content: String, intent: Intent? = null) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
createNotificationChannel(context)
val targetIntent = intent ?: Intent(context, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
}
val pendingIntent = PendingIntent.getActivity(
context,
0,
targetIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
val notification = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification_app_icon_white_24dp)
.setContentTitle(title)
.setContentText(content)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.build()
notificationManager.notify(NOTIFICATION_ID, notification)
}
fun showNotification(context: Context, titleResId: Int, contentResId: Int, intent: Intent? = null) {
val title = context.getString(titleResId)
val content = context.getString(contentResId)
showNotification(context, title, content, intent)
}
}
@@ -60,6 +60,7 @@ import com.michatec.radio.extensions.*
import com.michatec.radio.helpers.*
import com.michatec.radio.ui.LayoutHolder
import com.michatec.radio.ui.PlayerState
import com.michatec.radio.BuildConfig
import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
@@ -95,6 +96,11 @@ class PlayerFragment : Fragment(),
private var tempStationUuid: String = String()
private var itemTouchHelper: ItemTouchHelper? = null
// Check if the device running the app is an Android TV instance
private val isAndroidTV: Boolean by lazy {
context?.packageManager?.hasSystemFeature(PackageManager.FEATURE_LEANBACK) == true
}
/* Overrides onCreate from Fragment */
override fun onCreate(savedInstanceState: Bundle?) {
@@ -831,7 +837,7 @@ class PlayerFragment : Fragment(),
} else {
activity?.packageManager?.getPackageInfo(requireActivity().packageName, 0)?.versionName
}
if (latestVersion != current) {
if (latestVersion != current && !BuildConfig.IS_DEBUG_ENABLED) {
// We have an update available, tell our user about it
view?.let {
Snackbar.make(it, getString(R.string.app_name) + " " + latestVersion + " " + getString(R.string.snackbar_update_available), 10000)
@@ -849,6 +855,22 @@ class PlayerFragment : Fragment(),
R.color.default_neutral_white))
.show()
}
if (!isAndroidTV) {
val releaseUrl = getString(R.string.snackbar_url_app_home_page)
// Create the clean browser intent that will trigger ONLY when the notification is tapped
val updateIntent = Intent(Intent.ACTION_VIEW, Uri.parse(releaseUrl)).apply {
putExtra("SOURCE", "SELF")
}
NotificationSys.showNotification(
requireContext(),
"${getString(R.string.app_name)} $latestVersion",
getString(R.string.snackbar_update_available),
intent = updateIntent
)
}
}
}, { error ->
Log.w(TAG, "Update check failed", error)
@@ -9,7 +9,6 @@ import android.os.Bundle
import android.os.CountDownTimer
import android.util.Log
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.media.MediaBrowserServiceCompat.BrowserRoot.EXTRA_RECENT
import androidx.media3.cast.CastPlayer
import androidx.media3.common.*
import androidx.media3.common.util.UnstableApi
@@ -29,6 +28,7 @@ import com.google.common.collect.ImmutableList
import com.google.common.util.concurrent.Futures
import com.google.common.util.concurrent.ListenableFuture
import com.michatec.radio.core.Collection
import com.michatec.radio.core.Station
import com.michatec.radio.helpers.*
import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers.Main
@@ -132,7 +132,7 @@ class PlayerService : MediaLibraryService(), SharedPreferences.OnSharedPreferenc
context: Context,
enableFloatOutput: Boolean,
enableAudioTrackPlaybackParams: Boolean
): AudioSink? {
): AudioSink {
return DefaultAudioSink.Builder(context)
.setAudioProcessors(arrayOf(nativeAudioProcessor))
.build()
@@ -170,6 +170,14 @@ class PlayerService : MediaLibraryService(), SharedPreferences.OnSharedPreferenc
override fun getDuration(): Long {
return C.TIME_UNSET // this will hide progress bar for HLS stations in the notification
}
override fun seekToNext() {
playNextStation()
}
override fun seekToPrevious() {
playPreviousStation()
}
}
player.addListener(playerListener)
}
@@ -347,6 +355,37 @@ class PlayerService : MediaLibraryService(), SharedPreferences.OnSharedPreferenc
}
/* Switches to the next radio station in collection */
private fun playNextStation() {
val currentMediaId = player.currentMediaItem?.mediaId ?: PreferencesHelper.loadLastPlayedStationUuid()
val currentPosition = CollectionHelper.getStationPosition(collection, currentMediaId)
if (currentPosition != -1) {
val nextPosition = if (currentPosition < collection.stations.size - 1) currentPosition + 1 else 0
playStation(collection.stations[nextPosition])
}
}
/* Switches to the previous radio station in collection */
private fun playPreviousStation() {
val currentMediaId = player.currentMediaItem?.mediaId ?: PreferencesHelper.loadLastPlayedStationUuid()
val currentPosition = CollectionHelper.getStationPosition(collection, currentMediaId)
if (currentPosition != -1) {
val previousPosition = if (currentPosition > 0) currentPosition - 1 else collection.stations.size - 1
playStation(collection.stations[previousPosition])
}
}
/* Starts playback of a radio station */
private fun playStation(station: Station) {
val mediaItem = CollectionHelper.buildMediaItem(this, station)
player.setMediaItem(mediaItem)
player.prepare()
player.play()
}
/*
* Custom MediaSession Callback that handles player commands
*/
@@ -407,8 +446,8 @@ class PlayerService : MediaLibraryService(), SharedPreferences.OnSharedPreferenc
browser: MediaSession.ControllerInfo,
params: LibraryParams?
): ListenableFuture<LibraryResult<MediaItem>> {
return if (params?.extras?.containsKey(EXTRA_RECENT) == true) {
// special case: system requested media resumption via EXTRA_RECENT
return if (params?.isRecent == true) {
// special case: system requested media resumption via isRecent
playLastStation = true
Futures.immediateFuture(LibraryResult.ofItem(CollectionHelper.getRecent(this@PlayerService, collection), params))
} else {
@@ -552,8 +591,7 @@ class PlayerService : MediaLibraryService(), SharedPreferences.OnSharedPreferenc
stopSelf()
}
Player.STATE_READY -> {
// Playback is paused. For radio, we can stop the service to remove the notification.
stopSelf()
// Playback is paused. For radio, we keep the service running to allow resumption from headphones.
}
Player.STATE_BUFFERING -> {
// DO NOT stop the service while buffering (especially important for Cast)
@@ -562,17 +600,6 @@ class PlayerService : MediaLibraryService(), SharedPreferences.OnSharedPreferenc
}
}
override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) {
super.onPlayWhenReadyChanged(playWhenReady, reason)
if (!playWhenReady) {
// Only stop if not buffering and not ready to play (i.e. truly stopped/paused)
if (player.playbackState != Player.STATE_BUFFERING) {
stopSelf()
}
}
}
override fun onPlayerError(error: PlaybackException) {
super.onPlayerError(error)
Log.d(TAG, "PlayerError occurred: ${error.errorCodeName}")
@@ -2,6 +2,7 @@ package com.michatec.radio
import android.app.Application
import com.michatec.radio.helpers.AppThemeHelper
import com.michatec.radio.helpers.LanguageHelper
import com.michatec.radio.helpers.PreferencesHelper
import com.michatec.radio.helpers.PreferencesHelper.initPreferences
@@ -18,6 +19,7 @@ class Radio : Application() {
initPreferences()
// set Dark / Light theme state
AppThemeHelper.setTheme(PreferencesHelper.loadThemeSelection())
LanguageHelper.setLanguage(this, PreferencesHelper.loadSelectedLanguage())
}
}
@@ -17,10 +17,13 @@ import androidx.navigation.fragment.findNavController
import androidx.preference.*
import com.google.android.material.snackbar.Snackbar
import com.michatec.radio.dialogs.ErrorDialog
import com.michatec.radio.dialogs.LanguageSelectionDialog
import com.michatec.radio.dialogs.PresetSelectionDialog
import com.michatec.radio.dialogs.ThemeSelectionDialog
import com.michatec.radio.dialogs.YesNoDialog
import com.michatec.radio.helpers.*
import com.michatec.radio.NotificationSys
import android.content.pm.PackageManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.launch
@@ -31,12 +34,17 @@ import java.util.*
/*
* SettingsFragment class
*/
class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogListener, ThemeSelectionDialog.ThemeSelectionDialogListener, PresetSelectionDialog.PresetSelectionDialogListener {
class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogListener, ThemeSelectionDialog.ThemeSelectionDialogListener, PresetSelectionDialog.PresetSelectionDialogListener, LanguageSelectionDialog.LanguageSelectionDialogListener {
/* Define log tag */
private val TAG: String = SettingsFragment::class.java.simpleName
// Check if the device running the app is an Android TV instance
private val isAndroidTV: Boolean by lazy {
context?.packageManager?.hasSystemFeature(PackageManager.FEATURE_LEANBACK) == true
}
/* Overrides onViewCreated from PreferenceFragmentCompat */
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@@ -148,7 +156,7 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
// set up "Buffer Size" preference
val preferenceBufferSize = SwitchPreferenceCompat(activity as Context)
val preferenceBufferSize = MarqueeSwitchPreference(context)
preferenceBufferSize.title = getString(R.string.pref_buffer_size_title)
preferenceBufferSize.setIcon(R.drawable.ic_network_check_24dp)
preferenceBufferSize.key = Keys.PREF_LARGE_BUFFER_SIZE
@@ -158,18 +166,22 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
// set up "Edit Stream Address" preference
val preferenceEnableEditingStreamUri = SwitchPreferenceCompat(activity as Context)
val preferenceEnableEditingStreamUri = MarqueeSwitchPreference(context)
preferenceEnableEditingStreamUri.title = getString(R.string.pref_edit_station_stream_title)
preferenceEnableEditingStreamUri.setIcon(R.drawable.ic_music_note_24dp)
preferenceEnableEditingStreamUri.key = Keys.PREF_EDIT_STREAMS_URIS
preferenceEnableEditingStreamUri.summaryOn = getString(R.string.pref_edit_station_stream_summary_enabled)
preferenceEnableEditingStreamUri.summaryOff = getString(R.string.pref_edit_station_stream_summary_disabled)
preferenceEnableEditingStreamUri.setDefaultValue(PreferencesHelper.loadEditStreamUrisEnabled(context))
preferenceEnableEditingStreamUri.isEnabled = PreferencesHelper.loadEditStreamUrisEnabled(context)
preferenceEnableEditingStreamUri.isEnabled = if (PreferencesHelper.loadEditStationsEnabled(context)) {
true
} else {
PreferencesHelper.loadEditStreamUrisEnabled(context)
}
// set up "Edit Stations" preference
val preferenceEnableEditingGeneral = SwitchPreferenceCompat(activity as Context)
val preferenceEnableEditingGeneral = MarqueeSwitchPreference(context)
preferenceEnableEditingGeneral.title = getString(R.string.pref_edit_station_title)
preferenceEnableEditingGeneral.setIcon(R.drawable.ic_edit_24dp)
preferenceEnableEditingGeneral.key = Keys.PREF_EDIT_STATIONS
@@ -219,7 +231,7 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
// set up "Preset Selection" preference
val preferencePresetSelection = Preference(context)
preferencePresetSelection.title = getString(R.string.pref_preset_selection_title)
preferencePresetSelection.setIcon(R.drawable.ic_music_note_24dp)
preferencePresetSelection.setIcon(R.drawable.ic_presets_24dp)
preferencePresetSelection.key = Keys.PREF_PRESET_SELECTED
val presetSummary = currentPreset.ifEmpty {
getString(R.string.pref_preset_none)
@@ -236,7 +248,7 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
// set up "Equalizer" preference entry
val preferenceEqualizer = Preference(context)
preferenceEqualizer.title = getString(R.string.pref_equalizer_title)
preferenceEqualizer.setIcon(R.drawable.ic_music_note_24dp)
preferenceEqualizer.setIcon(R.drawable.ic_equalizer_24dp)
preferenceEqualizer.key = Keys.PREF_EQUALIZER
if (currentPreset.isEmpty()) {
preferenceEqualizer.summary = getString(R.string.pref_equalizer_summary)
@@ -252,7 +264,7 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
val preferenceVisualizer = Preference(context)
preferenceVisualizer.title = getString(R.string.pref_visualizer_title)
preferenceVisualizer.setIcon(R.drawable.ic_music_note_24dp)
preferenceVisualizer.setIcon(R.drawable.ic_visualizer_24dp)
preferenceVisualizer.summary = getString(R.string.pref_visualizer_summary)
preferenceVisualizer.setOnPreferenceClickListener {
findNavController().navigate(R.id.action_settings_to_visualizer)
@@ -304,6 +316,48 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
return@setOnPreferenceClickListener true
}
// set up "Test Notification" preference
val preferenceTestNotification = Preference(context)
preferenceTestNotification.title = getString(R.string.pref_test_notification_title)
preferenceTestNotification.setIcon(R.drawable.ic_notification_app_icon_24dp)
preferenceTestNotification.summary = getString(R.string.pref_test_notification_summary)
preferenceTestNotification.setOnPreferenceClickListener {
// show test notification
NotificationSys.showNotification(
context,
getString(R.string.pref_test_notification_title),
getString(R.string.notification_test_content)
)
return@setOnPreferenceClickListener true
}
// set up "Security" preference
val preferenceSecurity = Preference(context)
preferenceSecurity.title = getString(R.string.pref_security_title)
preferenceSecurity.setIcon(R.drawable.ic_security_24dp)
preferenceSecurity.summary = getString(R.string.pref_security_summary)
preferenceSecurity.setOnPreferenceClickListener {
// open web browser
val intent = Intent().apply {
action = Intent.ACTION_VIEW
data = "https://github.com/michatec/Radio/blob/master/SECURITY.md".toUri()
}
startActivity(intent)
return@setOnPreferenceClickListener true
}
val preferenceLanguageSelection = Preference(context)
preferenceLanguageSelection.title = getString(R.string.pref_language_selection_title)
preferenceLanguageSelection.setIcon(R.drawable.ic_language_24dp)
preferenceLanguageSelection.key = Keys.PREF_LANGUAGE_SELECTED
preferenceLanguageSelection.summary = "${getString(R.string.pref_language_selection_summary)}: ${
LanguageHelper.getCurrentLanguage(activity as Context)
}"
preferenceLanguageSelection.setOnPreferenceClickListener {
LanguageSelectionDialog(this).show(activity as Context)
return@setOnPreferenceClickListener true
}
// set preference categories
val preferenceCategoryGeneral = PreferenceCategory(activity as Context)
@@ -330,6 +384,11 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
screen.addPreference(preferenceCategoryGeneral)
preferenceCategoryGeneral.addPreference(preferenceThemeSelection)
preferenceCategoryGeneral.addPreference(preferenceLanguageSelection)
if (!isAndroidTV) {
preferenceCategoryGeneral.addPreference(preferenceTestNotification)
}
screen.addPreference(preferenceCategoryAudioEffects)
preferenceCategoryAudioEffects.addPreference(preferenceBassBoost)
@@ -357,6 +416,7 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
screen.addPreference(preferenceCategoryLinks)
preferenceCategoryLinks.addPreference(preferenceGitHub)
preferenceCategoryLinks.addPreference(preferenceLicense)
preferenceCategoryLinks.addPreference(preferenceSecurity)
preferenceScreen = screen
}
@@ -397,6 +457,20 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
}
}
/* Overrides onLanguageSelectionDialog from LanguageSelectionDialogListener */
override fun onLanguageSelectionDialog(dialogResult: Boolean, selectedLanguage: String) {
if (dialogResult) {
// update summary
val languagePreference = findPreference<Preference>(Keys.PREF_LANGUAGE_SELECTED)
val languageSummary = if (selectedLanguage.isEmpty()) {
getString(R.string.pref_language_system)
} else {
LanguageHelper.getCurrentLanguage(activity as Context)
}
languagePreference?.summary = "${getString(R.string.pref_language_selection_summary)}: $languageSummary"
}
}
/* Updates the enabled/disabled state of EQ controls based on preset selection */
private fun updateEqControlStates() {
val currentPreset = PreferencesHelper.loadSelectedPreset()
@@ -3,7 +3,6 @@ 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
@@ -11,12 +10,12 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.widget.CheckBox
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
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
@@ -180,7 +179,6 @@ class CollectionAdapter(
// get reference to StationViewHolder
val stationViewHolder: StationViewHolder = holder
// set up station views
setStarredIcon(stationViewHolder, station)
setStationName(stationViewHolder, station)
@@ -190,15 +188,12 @@ class CollectionAdapter(
setPlaybackProgress(stationViewHolder, station)
setDownloadProgress(stationViewHolder, station)
stationViewHolder.playButtonView.isGone = true
// highlight if reordering
if (reorderStationUuid == station.uuid) {
stationViewHolder.stationCardView.setStrokeColor(
ColorStateList.valueOf(
ContextCompat.getColor(context, R.color.cardview_reordering)
)
)
stationViewHolder.reorderCheckbox.isVisible = true
stationViewHolder.reorderCheckbox.isChecked = true
} else {
stationViewHolder.reorderCheckbox.isGone = true
stationViewHolder.reorderCheckbox.isChecked = false
}
// show / hide edit views
@@ -344,6 +339,7 @@ class CollectionAdapter(
/* Shows / hides the edit view for a station */
/* TODO: Remove @SuppressLint("NotifyDataSetChanged"), remove NotifyDataSetChanged */
@SuppressLint("NotifyDataSetChanged")
private fun toggleEditViews(position: Int, stationUuid: String) {
when (stationUuid) {
@@ -776,6 +772,7 @@ class CollectionAdapter(
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)
val reorderCheckbox: CheckBox = stationCardLayout.findViewById(R.id.reorder_checkbox)
val bufferingProgress: ProgressBar = stationCardLayout.findViewById(R.id.buffering_progress)
val downloadProgress: ProgressBar = stationCardLayout.findViewById(R.id.download_progress)
@@ -73,6 +73,7 @@ class FindStationDialog (
/* Overrides onRadioBrowserSearchResults from RadioBrowserSearchListener */
/* TODO: Remove @SuppressLint("NotifyDataSetChanged"), remove NotifyDataSetChanged */
@SuppressLint("NotifyDataSetChanged")
override fun onRadioBrowserSearchResults(results: Array<RadioBrowserResult>) {
if (results.isNotEmpty()) {
@@ -0,0 +1,140 @@
package com.michatec.radio.dialogs
import android.content.Context
import android.view.LayoutInflater
import android.widget.RadioButton
import android.widget.RadioGroup
import androidx.appcompat.app.AlertDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.michatec.radio.R
import com.michatec.radio.helpers.PreferencesHelper
/*
* LanguageSelectionDialog class
*/
class LanguageSelectionDialog(private var languageSelectionDialogListener: LanguageSelectionDialogListener) {
/* Interface used to communicate back to activity */
interface LanguageSelectionDialogListener {
fun onLanguageSelectionDialog(dialogResult: Boolean, selectedLanguage: String)
}
/* Main class variables */
private lateinit var dialog: AlertDialog
/* Data class representing a supported language */
data class Language(
val code: String,
val nameResId: Int
)
/* List of supported languages - displayed in their own language */
private val supportedLanguages = listOf(
Language("system", R.string.pref_language_system),
Language("en", R.string.pref_language_en),
Language("de", R.string.pref_language_de),
Language("fr", R.string.pref_language_fr),
Language("ru", R.string.pref_language_ru),
Language("ja", R.string.pref_language_ja),
Language("nl", R.string.pref_language_nl),
Language("pl", R.string.pref_language_pl),
Language("el", R.string.pref_language_el),
Language("da", R.string.pref_language_da)
)
/* Counter for generating unique view IDs */
private var viewIdCounter = 0x7F010001 // Starting after android.R.id.home
/* Construct and show dialog */
fun show(context: Context) {
// prepare dialog builder
val builder = MaterialAlertDialogBuilder(context)
// inflate custom layout
val inflater = LayoutInflater.from(context)
val view = inflater.inflate(R.layout.dialog_language_selection, null)
// find radio group
val radioGroup = view.findViewById<RadioGroup>(R.id.language_radio_group)
val currentLanguage = PreferencesHelper.loadSelectedLanguage()
// add radio buttons for each supported language
for (language in supportedLanguages) {
val radioButton = RadioButton(context).apply {
id = generateViewId()
tag = language.code
text = context.getString(language.nameResId)
textSize = if (isTelevision(context)) 20f else 16f
setPadding(dpToPx(context, 8), dpToPx(context, 16), dpToPx(context, 16), dpToPx(context, 16))
}
radioGroup.addView(radioButton)
}
// set current selection
for (i in 0 until radioGroup.childCount) {
val radioButton = radioGroup.getChildAt(i) as RadioButton
if (radioButton.tag == currentLanguage) {
radioButton.isChecked = true
break
}
}
// if no language is selected, check the first one (system)
if (radioGroup.checkedRadioButtonId == -1) {
val firstButton = radioGroup.getChildAt(0) as RadioButton
firstButton.isChecked = true
}
// set up radio group listener
radioGroup.setOnCheckedChangeListener { _, checkedId ->
val selectedButton = radioGroup.findViewById<RadioButton>(checkedId)
val selectedLanguageCode = selectedButton?.tag as? String ?: "system"
// save language selection to preferences
PreferencesHelper.saveSelectedLanguage(selectedLanguageCode)
// notify listener
languageSelectionDialogListener.onLanguageSelectionDialog(true, selectedLanguageCode)
// dismiss dialog
dialog.dismiss()
}
// set custom view
builder.setView(view)
// handle outside-click as cancel
builder.setOnCancelListener {
languageSelectionDialogListener.onLanguageSelectionDialog(false, "")
}
// display dialog
dialog = builder.create()
dialog.show()
}
/* Generate a unique view ID */
private fun generateViewId(): Int {
return viewIdCounter++
}
/* Helper function to check if device is a TV */
private fun isTelevision(context: Context): Boolean {
val uiMode = context.resources.configuration.uiMode
return (uiMode and android.content.res.Configuration.UI_MODE_TYPE_MASK) == android.content.res.Configuration.UI_MODE_TYPE_TELEVISION
}
/* Helper function to convert dp to pixels */
private fun dpToPx(context: Context, dp: Int): Int {
return (dp * context.resources.displayMetrics.density).toInt()
}
}
@@ -11,6 +11,7 @@ import android.widget.FrameLayout
import androidx.preference.Preference
import androidx.preference.PreferenceViewHolder
import com.michatec.radio.R
import androidx.core.view.isEmpty
class ExtrasHelper {
companion object {
@@ -69,7 +70,7 @@ class ExtrasHelper {
if (currentParent != container) {
currentParent?.removeView(visualizerView)
// If we injected into a standard preference, don't clear everything, just add
if (container is FrameLayout || container.childCount == 0) {
if (container is FrameLayout || container.isEmpty()) {
container.removeAllViews()
}
container.addView(visualizerView)
@@ -0,0 +1,63 @@
package com.michatec.radio.helpers
import android.app.Activity
import android.content.Context
import android.util.Log
import com.michatec.radio.R
import java.util.Locale
/*
* LanguageHelper object
*/
object LanguageHelper {
/* Define log tag */
private val TAG: String = LanguageHelper::class.java.simpleName
/* Sets the app language on the activity */
fun setLanguage(context: Context, languageCode: String): Boolean {
if (languageCode.isEmpty()) {
Log.i(TAG, "No language code provided, using system default")
return false
}
if (languageCode == "system") {
Log.i(TAG, "Reverting to system default locale")
if (context is Activity) {
context.recreate()
}
return true
}
val locale = Locale.forLanguageTag(languageCode)
Locale.setDefault(locale)
if (context is Activity) {
context.recreate()
}
Log.i(TAG, "Locale changed to: $languageCode")
return true
}
/* Returns a localized resources object */
fun getCurrentLanguage(context: Context): String {
return when (val languageCode = PreferencesHelper.loadSelectedLanguage()) {
"system" -> context.getString(R.string.pref_language_system)
"en" -> context.getString(R.string.pref_language_en)
"de" -> context.getString(R.string.pref_language_de)
"fr" -> context.getString(R.string.pref_language_fr)
"ru" -> context.getString(R.string.pref_language_ru)
"ja" -> context.getString(R.string.pref_language_ja)
"nl" -> context.getString(R.string.pref_language_nl)
"pl" -> context.getString(R.string.pref_language_pl)
"el" -> context.getString(R.string.pref_language_el)
"da" -> context.getString(R.string.pref_language_da)
else -> languageCode
}
}
}
@@ -16,7 +16,7 @@ class MarqueeSwitchPreference(context: Context) : SwitchPreferenceCompat(context
val title = holder.findViewById(android.R.id.title) as? TextView
title?.apply {
ellipsize = TextUtils.TruncateAt.MARQUEE
setSingleLine(true)
isSingleLine = true
marqueeRepeatLimit = -1 // Repeat indefinitely
isSelected = true // Required for marquee to start
setHorizontallyScrolling(true)
@@ -316,6 +316,16 @@ object PreferencesHelper {
}
}
/* Loads selected language */
fun loadSelectedLanguage(): String {
return sharedPreferences.getString(Keys.PREF_LANGUAGE_SELECTED, "system") ?: "system"
}
/* Saves selected language */
fun saveSelectedLanguage(language: String) {
sharedPreferences.edit { putString(Keys.PREF_LANGUAGE_SELECTED, language) }
}
/* Loads preset Bass Boost */
fun loadPresetBassBoost(): Float {
return sharedPreferences.getFloat(Keys.PREF_PRESET_BASS_BOOST, 0f)
@@ -171,7 +171,7 @@ class SearchResultAdapter(
context: Context,
enableFloatOutput: Boolean,
enableAudioTrackPlaybackParams: Boolean
): AudioSink? {
): AudioSink {
return DefaultAudioSink.Builder(context)
.setAudioProcessors(arrayOf(nativeAudioProcessor))
.build()
@@ -1,13 +1,12 @@
package com.michatec.radio.ui
import android.annotation.SuppressLint
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.drawable.AnimatedVectorDrawable
import android.os.Build
import java.util.Locale
import android.view.View
import android.view.animation.Animation
import android.view.animation.AnimationUtils
@@ -129,7 +128,6 @@ data class LayoutHolder(var rootView: View) {
/* Updates the player views */
@SuppressLint("DefaultLocale")
fun updatePlayerViews(context: Context, station: Station, isPlaying: Boolean) {
if (!isPlaying) {
metadataView?.text = station.name
@@ -162,9 +160,9 @@ data class LayoutHolder(var rootView: View) {
} else {
val kiloBytesPerSecond = station.bitrate / 8F
val dataRateString = if (kiloBytesPerSecond >= 1000) {
String.format("%.2f mb/s", kiloBytesPerSecond / 1000F)
String.format(Locale.ROOT, "%.2f mb/s", kiloBytesPerSecond / 1000F)
} else {
String.format("%.0f kb/s", kiloBytesPerSecond)
String.format(Locale.ROOT, "%.0f kb/s", kiloBytesPerSecond)
}
// show the bitrate and codec if the result is available in the radio-browser.info API
buildString {
@@ -190,36 +188,42 @@ data class LayoutHolder(var rootView: View) {
context,
sheetStreamingLinkView?.text ?: ""
)
Snackbar.make(rootView, R.string.toastmessage_copied_to_clipboard, Snackbar.LENGTH_LONG).show()
}
sheetStreamingLinkView?.setOnClickListener {
copyToClipboard(
context,
sheetStreamingLinkView?.text ?: ""
)
Snackbar.make(rootView, R.string.toastmessage_copied_to_clipboard, Snackbar.LENGTH_LONG).show()
}
sheetMetadataHistoryHeadline?.setOnClickListener {
copyToClipboard(
context,
sheetMetadataHistoryView?.text ?: ""
)
Snackbar.make(rootView, R.string.toastmessage_copied_to_clipboard, Snackbar.LENGTH_LONG).show()
}
sheetMetadataHistoryView?.setOnClickListener {
copyToClipboard(
context,
sheetMetadataHistoryView?.text ?: ""
)
Snackbar.make(rootView, R.string.toastmessage_copied_to_clipboard, Snackbar.LENGTH_LONG).show()
}
sheetCopyMetadataButtonView?.setOnClickListener {
copyToClipboard(
context,
sheetMetadataHistoryView?.text ?: ""
)
Snackbar.make(rootView, R.string.toastmessage_copied_to_clipboard, Snackbar.LENGTH_LONG).show()
}
sheetBitrateView?.setOnClickListener {
copyToClipboard(
context,
sheetBitrateView?.text ?: ""
)
Snackbar.make(rootView, R.string.toastmessage_copied_to_clipboard, Snackbar.LENGTH_LONG).show()
}
sheetShareLinkButtonView?.setOnClickListener {
val share = Intent.createChooser(Intent().apply {
@@ -238,10 +242,7 @@ data class LayoutHolder(var rootView: View) {
val clip: ClipData = ClipData.newPlainText("simple text", clipString)
val cm: ClipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
cm.setPrimaryClip(clip)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
// since API 33 (TIRAMISU) the OS displays its own notification when content is copied to the clipboard
Snackbar.make(rootView, R.string.toastmessage_copied_to_clipboard, Snackbar.LENGTH_LONG).show()
}
Snackbar.make(rootView, R.string.toastmessage_copied_to_clipboard, Snackbar.LENGTH_LONG).show()
}
@@ -251,6 +252,7 @@ data class LayoutHolder(var rootView: View) {
val stringBuilder: StringBuilder = StringBuilder()
metadataHistory.forEach { stringBuilder.append("${it.trim()}\n") }
copyToClipboard(rootView.context, stringBuilder.toString())
Snackbar.make(rootView, R.string.toastmessage_copied_to_clipboard, Snackbar.LENGTH_LONG).show()
}
@@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Focused (TV remote) states -->
<item android:color="@color/list_card_stroke_focused" android:state_focused="true" />
<!-- Default state -->
<item android:color="@color/list_card_stroke_background" />
</selector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:viewportHeight="960"
android:viewportWidth="960"
android:width="24dp">
<path
android:fillColor="@color/icon_default"
android:pathData="M200,840Q167,840 143.5,816.5Q120,793 120,760L120,200Q120,167 143.5,143.5Q167,120 200,120L760,120Q793,120 816.5,143.5Q840,167 840,200L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840ZM240,503L400,343L560,503L720,343L760,383L760,200Q760,200 760,200Q760,200 760,200L200,200Q200,200 200,200Q200,200 200,200L200,463L240,503ZM200,760L760,760Q760,760 760,760Q760,760 760,760L760,496L720,456L560,616L400,456L240,616L200,576L200,760Q200,760 200,760Q200,760 200,760ZM200,760L200,760Q200,760 200,760Q200,760 200,760L200,496L200,576L200,463L200,383L200,200Q200,200 200,200Q200,200 200,200L200,200Q200,200 200,200Q200,200 200,200L200,463L200,463L200,576L200,576L200,760Q200,760 200,760Q200,760 200,760Z"/>
</vector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@color/icon_default"
android:pathData="M280,680L560,680L560,600L280,600L280,680ZM280,520L680,520L680,440L280,440L280,520ZM280,360L680,360L680,280L280,280L280,360ZM200,840Q167,840 143.5,816.5Q120,793 120,760L120,200Q120,167 143.5,143.5Q167,120 200,120L760,120Q793,120 816.5,143.5Q840,167 840,200L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840ZM200,760L760,760Q760,760 760,760Q760,760 760,760L760,200Q760,200 760,200Q760,200 760,200L200,200Q200,200 200,200Q200,200 200,200L200,760Q200,760 200,760Q200,760 200,760ZM200,200L200,200Q200,200 200,200Q200,200 200,200L200,760Q200,760 200,760Q200,760 200,760L200,760Q200,760 200,760Q200,760 200,760L200,200Q200,200 200,200Q200,200 200,200Z" />
</vector>
File diff suppressed because one or more lines are too long
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:viewportHeight="960"
android:viewportWidth="960"
android:width="24dp">
<path
android:fillColor="@color/icon_default"
android:pathData="M200,840Q167,840 143.5,816.5Q120,793 120,760L120,200Q120,167 143.5,143.5Q167,120 200,120L760,120Q793,120 816.5,143.5Q840,167 840,200L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840ZM200,333L760,333L760,200Q760,200 760,200Q760,200 760,200L200,200Q200,200 200,200Q200,200 200,200L200,333ZM200,547L760,547L760,413L200,413L200,547ZM200,760L760,760Q760,760 760,760Q760,760 760,760L760,627L200,627L200,760Q200,760 200,760Q200,760 200,760ZM240,306L240,226L320,226L320,306L240,306ZM240,520L240,440L320,440L320,520L240,520ZM240,734L240,654L320,654L320,734L240,734Z"/>
</vector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@color/icon_default"
android:pathData="M12,1L3,5v6c0,5.55 3.84,10.74 9,12c5.16-1.26 9-6.45 9-12V5l-9,-4z"/>
</vector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:viewportHeight="960"
android:viewportWidth="960"
android:width="24dp">
<path
android:fillColor="@color/icon_default"
android:pathData="M640,800L640,520L800,520L800,800L640,800ZM400,800L400,160L560,160L560,800L400,800ZM160,800L160,360L320,360L320,800L160,800Z"/>
</vector>
@@ -78,6 +78,9 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginHorizontal="16dp"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
android:singleLine="true"
android:textAlignment="center"
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
android:textColor="@color/player_sheet_text_main"
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="24dp">
<TextView
android:id="@+id/dialog_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/pref_language_selection_title"
android:textSize="24sp"
android:textStyle="bold"
android:paddingBottom="16dp" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical">
<RadioGroup
android:id="@+id/language_radio_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
</RadioGroup>
</ScrollView>
</LinearLayout>
@@ -72,10 +72,11 @@
<com.google.android.material.textview.MaterialTextView
android:id="@+id/player_station_metadata"
android:marqueeRepeatLimit="marquee_forever"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:ellipsize="end"
android:ellipsize="marquee"
android:singleLine="true"
android:textAlignment="center"
android:textAppearance="@style/TextAppearance.Material3.BodyLarge"
@@ -56,7 +56,8 @@
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:layout_marginBottom="8dp"
android:ellipsize="end"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
android:letterSpacing="0"
android:singleLine="true"
android:textAlignment="center"
@@ -34,7 +34,7 @@
android:clickable="true"
android:stateListAnimator="@null"
app:backgroundTint="@color/list_card_background"
app:icon="@drawable/ic_music_note_24dp"
app:icon="@drawable/ic_visualizer_24dp"
app:iconTint="@color/icon_default"
app:rippleColor="@color/list_card_stroke_background"
app:strokeColor="@color/list_card_stroke_background"
+12 -1
View File
@@ -61,6 +61,17 @@
app:shapeAppearanceOverlay="@style/RoundedCorners"
app:srcCompat="@drawable/ic_image_white_36dp" />
<CheckBox
android:id="@+id/reorder_checkbox"
android:layout_width="24dp"
android:layout_height="24dp"
android:foregroundTint="@color/icon_default"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/station_icon"
app:layout_constraintStart_toEndOf="@+id/starred_icon"
app:layout_constraintTop_toTopOf="@+id/station_icon"
tools:visibility="visible" />
<ImageView
android:id="@+id/starred_icon"
android:layout_width="wrap_content"
@@ -86,7 +97,7 @@
android:textColor="@color/text_lightweight"
app:layout_constraintBottom_toBottomOf="@+id/station_icon"
app:layout_constraintEnd_toStartOf="@+id/playback_button"
app:layout_constraintStart_toEndOf="@+id/starred_icon"
app:layout_constraintStart_toEndOf="@+id/reorder_checkbox"
app:layout_constraintTop_toTopOf="@+id/station_icon"
tools:text="@string/sample_text_station_name" />
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/dialog_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/pref_language_selection_title"
android:textSize="18sp"
android:textStyle="bold"
android:paddingBottom="12dp" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical">
<RadioGroup
android:id="@+id/language_radio_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
</RadioGroup>
</ScrollView>
</LinearLayout>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_banner_background"/>
<foreground android:drawable="@mipmap/ic_banner_foreground"/>
</adaptive-icon>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_channel_background"/>
<foreground android:drawable="@mipmap/ic_channel_foreground"/>
</adaptive-icon>
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

+49
View File
@@ -44,6 +44,8 @@
<string name="notification_stop">Stop</string>
<string name="notification_skip_to_previous">Forrige</string>
<string name="notification_skip_to_next">Næste</string>
<string name="notification_test_content">Dette er en testmeddelelse.</string>
<string name="notification_channel_description">Alle meddelelser om appen.</string>
<!-- Onboarding -->
<string name="onboarding_app_description">Fordyb dig i lyden du elsker!</string>
<string name="onboarding_app_get_started">Kom i gang</string>
@@ -60,6 +62,7 @@
<string name="pref_buffer_size_title">Brug større buffer</string>
<string name="pref_edit_station_stream_summary_disabled">Redigering af stream-links er deaktiveret.</string>
<string name="pref_edit_station_stream_summary_enabled">Redigering af stream-links er aktiveret. Sørg for at angive en korrekt streamadresse.</string>
<string name="pref_edit_station_stream_title">Rediger stream-links</string>
<string name="pref_edit_station_summary_disabled">Redigering af stationsoplysninger er deaktiveret.</string>
<string name="pref_edit_station_summary_enabled">Redigering er aktiveret. Langt tryk for at redigere.</string>
<string name="pref_edit_station_title">Rediger station</string>
@@ -83,6 +86,8 @@
<string name="pref_theme_selection_title">App-tema</string>
<string name="pref_update_station_images_summary">Download nyeste stationsbilleder.</string>
<string name="pref_update_station_images_title">Opdater stationsbilleder</string>
<string name="pref_test_notification_title">Testmeddelelse</string>
<string name="pref_test_notification_summary">Test om meddelelsessystemet virker.</string>
<!-- App-genveje -->
<string name="shortcut_last_station_disabled_message">Genvej til seneste station er deaktiveret.</string>
<string name="shortcut_last_station_long_label">Afspil seneste station</string>
@@ -110,4 +115,48 @@
<!-- Snackbars -->
<string name="snackbar_show">Vis</string>
<string name="snackbar_update_available">er tilgængelig!</string>
<string name="snackbar_failed_permission_notification">Kunne ikke anmode om meddelelsestilladelse.</string>
<!-- Language Selection -->
<string name="pref_language_selection_title">Sprog</string>
<string name="pref_language_selection_summary">Aktuelt sprog</string>
<string name="pref_language_system">🗺️ System</string>
<!-- Settings -->
<string name="pref_update_collection_title">Opdater stationer</string>
<string name="pref_update_collection_summary">Download den nyeste version af alle stationer.</string>
<string name="dialog_yes_no_message_update_collection">Download den nyeste version af alle stationer?</string>
<string name="dialog_yes_no_positive_button_update_collection">Opdater</string>
<string name="pref_audio_effects_title">Lydeffekter</string>
<string name="pref_bass_boost_title">Bas-forstærkning</string>
<string name="pref_bass_boost_summary">Øg basforstærkningen.</string>
<string name="pref_security_title">Sikkerhed</string>
<string name="pref_security_summary">Lær mere om sikkerheden for denne applikation</string>
<string name="pref_reverb_title">Hall</string>
<string name="pref_reverb_summary">Juster hall-blanding.</string>
<string name="pref_drc_title">Dynamisk rækkeviddekomprimering</string>
<string name="pref_drc_summary">Komprimer det dynamiske område for konsistent lydstyrke.</string>
<string name="pref_eq_low_title">Equalizer: 31 Hz</string>
<string name="pref_eq_mid_title">Equalizer: 125 Hz</string>
<string name="pref_eq_high_title">Equalizer: 4 kHz</string>
<string name="pref_eq_band_1_title">Equalizer: 62 Hz</string>
<string name="pref_eq_band_2_title">Equalizer: 250 Hz</string>
<string name="pref_eq_band_3_title">Equalizer: 500 Hz</string>
<string name="pref_eq_band_4_title">Equalizer: 1 kHz</string>
<string name="pref_eq_band_5_title">Equalizer: 2 kHz</string>
<string name="pref_eq_band_6_title">Equalizer: 8 kHz</string>
<string name="pref_eq_band_7_title">Equalizer: 16 kHz</string>
<string name="pref_equalizer_title">Equalizer</string>
<string name="pref_equalizer_summary">Juster lydfrekvenserne.</string>
<string name="pref_equalizer_summary_off">Justering af lydfrekvenserne er deaktiveret.</string>
<string name="pref_equalizer_reset_title">Nulstil equalizer</string>
<string name="pref_preset_selection_title">Vælg forudindstilling</string>
<string name="pref_preset_selection_summary">Vælg en lydforudindstilling</string>
<string name="pref_preset_none">Ingen (Manuel)</string>
<string name="pref_preset_rock">Rock</string>
<string name="pref_preset_pop">Pop</string>
<string name="pref_preset_jazz">Jazz</string>
<string name="pref_preset_flat">Flad</string>
<string name="loading">Indlæser…</string>
<string name="media_route_menu_title">Cast</string>
<string name="pref_visualizer_title">Spektrumanalysator</string>
<string name="pref_visualizer_summary">Vis spektrumanalysatoren.</string>
</resources>
+14
View File
@@ -44,6 +44,8 @@
<string name="notification_stop">Stopp</string>
<string name="notification_skip_to_previous">Zurück</string>
<string name="notification_skip_to_next">Nächste</string>
<string name="notification_test_content">Dies ist eine Testbenachrichtigung.</string>
<string name="notification_channel_description">Alle Benachrichtigungen über die App.</string>
<!-- Onboarding -->
<string name="onboarding_app_description">Tauche ein in den Sound deiner Wahl!</string>
<string name="onboarding_app_get_started">Jetzt starten</string>
@@ -69,6 +71,11 @@
<string name="pref_edit_station_summary_enabled">Die Bearbeitung von Senderinformationen ist aktiviert. Drücke lange, um den Bearbeitungsmodus aufzurufen.</string>
<string name="pref_edit_station_title">Sender bearbeiten</string>
<string name="pref_general_title">Allgemein</string>
<!-- Language Selection -->
<string name="pref_language_selection_title">Sprache</string>
<string name="pref_language_selection_summary">Aktuelle Sprache</string>
<string name="pref_language_system">🗺️ System</string>
<string name="pref_language_en" translatable="false">🇬🇧 Englisch</string>
<string name="pref_license_title">Diese Anwendung ist Open Source</string>
<string name="pref_license_summary">Lizenziert unter der GPLv3 License</string>
<string name="pref_links_title">Links</string>
@@ -88,6 +95,8 @@
<string name="pref_theme_selection_title">App-Design</string>
<string name="pref_update_station_images_summary">Die neueste Version aller Senderbilder herunterladen.</string>
<string name="pref_update_station_images_title">Senderbilder aktualisieren</string>
<string name="pref_test_notification_title">Test-Benachrichtigung</string>
<string name="pref_test_notification_summary">Testen, ob das Benachrichtigungssystem funktioniert.</string>
<!-- Sample Text -->
<!-- App Shortcuts -->
<string name="shortcut_last_station_disabled_message">Verknüpfung für Wiedergabe des letzten Senders deaktiviert.</string>
@@ -117,10 +126,13 @@
<!-- Snackbars -->
<string name="snackbar_show">Zeigen</string>
<string name="snackbar_update_available">ist verfügbar!</string>
<string name="snackbar_failed_permission_notification">Fehler bei der Anfrage nach Benachrichtigungsberechtigung.</string>
<string name="pref_audio_effects_title">Audio-Effekte</string>
<string name="pref_bass_boost_title">Bass-Boost</string>
<string name="pref_bass_boost_summary">Erhöhen Sie die Bassverstärkung.</string>
<string name="pref_reverb_title">Hall</string>
<string name="pref_security_title">Sicherheit</string>
<string name="pref_security_summary">Erfahren Sie mehr über die Sicherheit dieser Anwendung</string>
<string name="pref_reverb_summary">Reverb-Mix anpassen.</string>
<string name="pref_drc_title">Dynamikkompression</string>
<string name="pref_drc_summary">Den Dynamikbereich für eine gleichbleibende Lautstärke komprimieren.</string>
@@ -145,6 +157,8 @@
<string name="pref_preset_pop">Pop</string>
<string name="pref_preset_jazz">Jazz</string>
<string name="pref_preset_flat">Flach</string>
<string name="media_route_menu_title">Streamen</string>
<string name="pref_visualizer_title">Spektrumanzeige</string>
<string name="pref_visualizer_summary">Sehe die Spektrumanzeige.</string>
<string name="loading">Lade…</string>
</resources>
+52
View File
@@ -16,6 +16,7 @@
<string name="descr_player_playback_button">Αναπαραγωγή/Παύση</string>
<string name="descr_player_station_image">Εικόνα σταθμού</string>
<!-- Dialogs -->
<string name="dialog_add_station_title">Προσθήκη Σταθμού</string>
<string name="dialog_edit_station_name">Όνομα σταθμού</string>
<string name="dialog_edit_stream_uri">Διεύθυνση ροής</string>
<string name="dialog_error_message_default">Προέκυψε ένα σφάλμα</string>
@@ -29,6 +30,7 @@
<string name="dialog_generic_button_okay">ΟΚ</string>
<string name="dialog_generic_details_button">Εμφάνιση λεπτομερειών</string>
<string name="dialog_opml_import_details_default">Δεν υπάρχουν διαθέσιμες λεπτομέρειες</string>
<string name="dialog_restore_collection_replace_existing">Αντικατάσταση της τρέχουσας συλλογής ραδιοφωνικών σταθμών με τον ραδιοφωνικό σταθμό από το αντίγραφο ασφαλείας;</string>
<string name="dialog_yes_no_message_remove_station">Αφαίρεση αυτού του σταθμού;</string>
<string name="dialog_yes_no_message_update_station_images">Να κατεβεί η τελευταία έκδοση όλων των εικόνων σταθμών;</string>
<string name="dialog_yes_no_positive_button_default">Ναι</string>
@@ -42,7 +44,10 @@
<string name="notification_stop">Διακοπή</string>
<string name="notification_skip_to_previous">Προηγούμενο</string>
<string name="notification_skip_to_next">Επόμενο</string>
<string name="notification_test_content">Αυτή είναι μια δοκιμαστική ειδοποίηση.</string>
<string name="notification_channel_description">Όλες οι ειδοποιήσεις σχετικά με την εφαρμογή.</string>
<!-- Onboarding -->
<string name="onboarding_app_description">Βυθιστείτε στον ήχο της επιλογής σας!</string>
<string name="onboarding_app_get_started">Ας ξεκινήσουμε</string>
<!-- Player -->
<string name="player_sheet_h2_station_metadata">Παίζεται τώρα</string>
@@ -81,6 +86,8 @@
<string name="pref_theme_selection_title">Θέμα Εφαρμογής</string>
<string name="pref_update_station_images_summary">Κατεβάστε την τελευταία έκδοση των εικόνων όλων των σταθμών.</string>
<string name="pref_update_station_images_title">Ενημέρωση Εικόνων Σταθμών</string>
<string name="pref_test_notification_title">Δοκιμαστική Ειδοποίηση</string>
<string name="pref_test_notification_summary">Έλεγχος αν το σύστημα ειδοποιήσεων λειτουργεί.</string>
<!-- Sample Text -->
<!-- App Shortcuts -->
<string name="shortcut_last_station_disabled_message">Η συντόμευση για την αναπαραγωγή του τελευταίου σταθμού έχει απενεργοποιηθεί.</string>
@@ -91,6 +98,7 @@
<!-- Toasts -->
<string name="toastmessage_backed_up">έχει δημιουργηθεί επιτυχώς το αντίγραφο ασφαλείας.</string>
<string name="toastmessage_copied_to_clipboard">Αντιγράφηκε στο πρόχειρο.</string>
<string name="toastmessage_connection_failed">Η σύνδεση δεν μπόρεσε να δημιουργηθεί ή να επαναφερθεί.</string>
<string name="toastmessage_error_download_error">Σφάλμα λήψης</string>
<string name="toastmessage_error_restart_playback_failed">Αδυναμία εκκίνησης ή επανεκκίνησης της αναπαραγωγής.</string>
<string name="toastmessage_install_file_helper">Παρακαλώ εγκαταστήστε ένα πρόγραμμα διαχείρισης αρχείων.</string>
@@ -109,4 +117,48 @@
<!-- Snackbars -->
<string name="snackbar_show">Εμφάνισε</string>
<string name="snackbar_update_available">είναι διαθέσιμη!</string>
<string name="snackbar_failed_permission_notification">Απέτυχε η αίτηση δικαιώματος ειδοποίησης.</string>
<!-- Language Selection -->
<string name="pref_language_selection_title">Γλώσσα</string>
<string name="pref_language_selection_summary">Τρέχουσα γλώσσα</string>
<string name="pref_language_system">🗺️ Σύστημα</string>
<!-- Settings -->
<string name="pref_update_collection_title">Ενημέρωση Σταθμών</string>
<string name="pref_update_collection_summary">Κατεβάστε την τελευταία έκδοση όλων των σταθμών.</string>
<string name="dialog_yes_no_message_update_collection">Κατεβάστε την τελευταία έκδοση όλων των σταθμών;</string>
<string name="dialog_yes_no_positive_button_update_collection">Ενημέρωση</string>
<string name="pref_audio_effects_title">Ηχητικά Εφέ</string>
<string name="pref_security_title">Ασφάλεια</string>
<string name="pref_security_summary">Μάθετε περισσότερα για την ασφάλεια αυτής της εφαρμογής</string>
<string name="pref_bass_boost_title">Ενίσχυση Μπάσων</string>
<string name="pref_bass_boost_summary">Αύξηση της ενίσχυσης μπάσων.</string>
<string name="pref_reverb_title">Αντήχηση</string>
<string name="pref_reverb_summary">Προσαρμογή μίξης αντήχησης.</string>
<string name="pref_drc_title">Συμπίεση Δυναμικού Εύρους</string>
<string name="pref_drc_summary">Συμπίεση δυναμικού εύρους για σταθερή ένταση.</string>
<string name="pref_eq_low_title">Ισοσταθμιστής: 31 Hz</string>
<string name="pref_eq_mid_title">Ισοσταθμιστής: 125 Hz</string>
<string name="pref_eq_high_title">Ισοσταθμιστής: 4 kHz</string>
<string name="pref_eq_band_1_title">Ισοσταθμιστής: 62 Hz</string>
<string name="pref_eq_band_2_title">Ισοσταθμιστής: 250 Hz</string>
<string name="pref_eq_band_3_title">Ισοσταθμιστής: 500 Hz</string>
<string name="pref_eq_band_4_title">Ισοσταθμιστής: 1 kHz</string>
<string name="pref_eq_band_5_title">Ισοσταθμιστής: 2 kHz</string>
<string name="pref_eq_band_6_title">Ισοσταθμιστής: 8 kHz</string>
<string name="pref_eq_band_7_title">Ισοσταθμιστής: 16 kHz</string>
<string name="pref_equalizer_title">Ισοσταθμιστής</string>
<string name="pref_equalizer_summary">Προσαρμογή ηχητικών συχνοτήτων.</string>
<string name="pref_equalizer_summary_off">Η προσαρμογή ηχητικών συχνοτήτων είναι απενεργοποιημένη.</string>
<string name="pref_equalizer_reset_title">Επαναφορά Ισοσταθμιστή</string>
<string name="pref_preset_selection_title">Επιλογή Προκαθορισμένου</string>
<string name="pref_preset_selection_summary">Επιλέξτε ένα ηχητικό προκαθορισμένο.</string>
<string name="pref_preset_none">Κανένα (Χειροκίνητο)</string>
<string name="pref_preset_rock">Rock</string>
<string name="pref_preset_pop">Pop</string>
<string name="pref_preset_jazz">Jazz</string>
<string name="pref_preset_flat">Επίπεδο</string>
<string name="loading">Φόρτωση…</string>
<string name="media_route_menu_title">Μετάδοση</string>
<string name="pref_visualizer_title">Αναλυτής Φάσματος</string>
<string name="pref_visualizer_summary">Εμφάνιση του Αναλυτή Φάσματος.</string>
</resources>
+49
View File
@@ -44,6 +44,8 @@
<string name="notification_stop">Arrêt</string>
<string name="notification_skip_to_previous">Précédent</string>
<string name="notification_skip_to_next">Suivant</string>
<string name="notification_test_content">Il sagit dune notification de test.</string>
<string name="notification_channel_description">Toutes les notifications de lapplication.</string>
<!-- Onboarding -->
<string name="onboarding_app_description">Plongez dans le son de votre choix !</string>
<string name="onboarding_app_get_started">Commencer maintenant</string>
@@ -60,6 +62,7 @@
<string name="pref_buffer_size_title">Utiliser un tampon plus grand</string>
<string name="pref_edit_station_stream_summary_disabled">La modification des liens de streaming est désactivée.</string>
<string name="pref_edit_station_stream_summary_enabled">La modification des liens de streaming est activée. Assurez-vous dentrer une adresse de flux correcte.</string>
<string name="pref_edit_station_stream_title">Modifier les liens de streaming</string>
<string name="pref_edit_station_summary_disabled">La modification des informations de la station est désactivée.</string>
<string name="pref_edit_station_summary_enabled">La modification est activée. Maintenez appuyé pour éditer.</string>
<string name="pref_edit_station_title">Modifier la station</string>
@@ -83,6 +86,8 @@
<string name="pref_theme_selection_title">Thème de lapplication</string>
<string name="pref_update_station_images_summary">Télécharger la dernière version de toutes les images des stations.</string>
<string name="pref_update_station_images_title">Mettre à jour les images des stations</string>
<string name="pref_test_notification_title">Notification de test</string>
<string name="pref_test_notification_summary">Tester si le système de notification fonctionne.</string>
<!-- Raccourcis de l'app -->
<string name="shortcut_last_station_disabled_message">Raccourci pour lire la dernière station désactivé.</string>
<string name="shortcut_last_station_long_label">Lire la dernière station</string>
@@ -110,4 +115,48 @@
<!-- Snackbars -->
<string name="snackbar_show">Afficher</string>
<string name="snackbar_update_available">est disponible !</string>
<string name="snackbar_failed_permission_notification">Échec de la demande dautorisation de notification.</string>
<!-- Language Selection -->
<string name="pref_language_selection_title">Langue</string>
<string name="pref_language_selection_summary">Langue actuelle</string>
<string name="pref_language_system">🗺️ Système</string>
<!-- Settings -->
<string name="pref_update_collection_title">Mettre à jour les stations</string>
<string name="pref_update_collection_summary">Téléchargez la dernière version de toutes les stations.</string>
<string name="dialog_yes_no_message_update_collection">Téléchargez la dernière version de toutes les stations ?</string>
<string name="dialog_yes_no_positive_button_update_collection">Mettre à jour</string>
<string name="pref_audio_effects_title">Effets Audio</string>
<string name="pref_bass_boost_title">Amplification des basses</string>
<string name="pref_bass_boost_summary">Augmenter l amplification des basses.</string>
<string name="pref_security_title">Sécurité</string>
<string name="pref_security_summary">En savoir plus sur la sécurité de cette application</string>
<string name="pref_reverb_title">Réverbération</string>
<string name="pref_reverb_summary">Ajuster le mix de réverbération.</string>
<string name="pref_drc_title">Compression Dynamique</string>
<string name="pref_drc_summary">Compresser la plage dynamique pour un volume constant.</string>
<string name="pref_eq_low_title">Égaliseur : 31 Hz</string>
<string name="pref_eq_mid_title">Égaliseur : 125 Hz</string>
<string name="pref_eq_high_title">Égaliseur : 4 kHz</string>
<string name="pref_eq_band_1_title">Égaliseur : 62 Hz</string>
<string name="pref_eq_band_2_title">Égaliseur : 250 Hz</string>
<string name="pref_eq_band_3_title">Égaliseur : 500 Hz</string>
<string name="pref_eq_band_4_title">Égaliseur : 1 kHz</string>
<string name="pref_eq_band_5_title">Égaliseur : 2 kHz</string>
<string name="pref_eq_band_6_title">Égaliseur : 8 kHz</string>
<string name="pref_eq_band_7_title">Égaliseur : 16 kHz</string>
<string name="pref_equalizer_title">Égaliseur</string>
<string name="pref_equalizer_summary">Ajuster les fréquences audio.</string>
<string name="pref_equalizer_summary_off">L ajustement des fréquences audio est désactivé.</string>
<string name="pref_equalizer_reset_title">Réinitialiser l égaliseur</string>
<string name="pref_preset_selection_title">Sélectionner un préréglage</string>
<string name="pref_preset_selection_summary">Choisissez un préréglage audio.</string>
<string name="pref_preset_none">Aucun (Manuel)</string>
<string name="pref_preset_rock">Rock</string>
<string name="pref_preset_pop">Pop</string>
<string name="pref_preset_jazz">Jazz</string>
<string name="pref_preset_flat">Plat</string>
<string name="loading">Chargement…</string>
<string name="media_route_menu_title">Diffuser</string>
<string name="pref_visualizer_title">Analyseur de spectre</string>
<string name="pref_visualizer_summary">Afficher l analyseur de spectre.</string>
</resources>
+49
View File
@@ -44,6 +44,8 @@
<string name="notification_stop">停止</string>
<string name="notification_skip_to_previous">前へ</string>
<string name="notification_skip_to_next">次へ</string>
<string name="notification_test_content">テスト通知です。</string>
<string name="notification_channel_description">アプリに関するすべての通知。</string>
<!-- オンボーディング -->
<string name="onboarding_app_description">お気に入りのサウンドの世界に飛び込もう!</string>
<string name="onboarding_app_get_started">今すぐ始める</string>
@@ -60,6 +62,7 @@
<string name="pref_buffer_size_title">大きなバッファを使用</string>
<string name="pref_edit_station_stream_summary_disabled">ストリームリンクの編集は無効です。</string>
<string name="pref_edit_station_stream_summary_enabled">ストリームリンクの編集は有効です。正しいURLを入力してください。</string>
<string name="pref_edit_station_stream_title">ストリームリンクを編集</string>
<string name="pref_edit_station_summary_disabled">局情報の編集は無効です。</string>
<string name="pref_edit_station_summary_enabled">局情報の編集は有効です。長押しで編集モードに入ります。</string>
<string name="pref_edit_station_title">局を編集</string>
@@ -83,6 +86,8 @@
<string name="pref_theme_selection_title">アプリテーマ</string>
<string name="pref_update_station_images_summary">すべての局画像を最新に更新します。</string>
<string name="pref_update_station_images_title">局画像を更新</string>
<string name="pref_test_notification_title">テスト通知</string>
<string name="pref_test_notification_summary">通知システムが動作するかテストします。</string>
<!-- ショートカット -->
<string name="shortcut_last_station_disabled_message">最後に再生した局のショートカットは無効になっています。</string>
<string name="shortcut_last_station_long_label">最後の局を再生</string>
@@ -111,4 +116,48 @@
<!-- スナックバー -->
<string name="snackbar_show">表示</string>
<string name="snackbar_update_available">が利用可能です!</string>
<string name="snackbar_failed_permission_notification">通知の権限リクエストに失敗しました。</string>
<!-- 言語選択 -->
<string name="pref_language_selection_title">言語</string>
<string name="pref_language_selection_summary">現在の言語</string>
<string name="pref_language_system">🗺️ システム</string>
<!-- 設定 -->
<string name="pref_update_collection_title">局を更新</string>
<string name="pref_update_collection_summary">すべての局の最新バージョンをダウンロードします。</string>
<string name="dialog_yes_no_message_update_collection">すべての局の最新バージョンをダウンロードしますか?</string>
<string name="dialog_yes_no_positive_button_update_collection">更新</string>
<string name="pref_audio_effects_title">オーディオエフェクト</string>
<string name="pref_bass_boost_title">バスブースト</string>
<string name="pref_security_title">セキュリティ</string>
<string name="pref_security_summary">このアプリケーションのセキュリティについて詳しく知る</string>
<string name="pref_bass_boost_summary">低音を増強します。</string>
<string name="pref_reverb_title">リバーブ</string>
<string name="pref_reverb_summary">リバーブミスを調整します。</string>
<string name="pref_drc_title">ダイナミックレンジ圧縮</string>
<string name="pref_drc_summary">音量的一定のためダイナミックレンジを圧縮します。</string>
<string name="pref_eq_low_title">イコライザー:31 Hz</string>
<string name="pref_eq_mid_title">イコライザー:125 Hz</string>
<string name="pref_eq_high_title">イコライザー:4 kHz</string>
<string name="pref_eq_band_1_title">イコライザー:62 Hz</string>
<string name="pref_eq_band_2_title">イコライザー:250 Hz</string>
<string name="pref_eq_band_3_title">イコライザー:500 Hz</string>
<string name="pref_eq_band_4_title">イコライザー:1 kHz</string>
<string name="pref_eq_band_5_title">イコライザー:2 kHz</string>
<string name="pref_eq_band_6_title">イコライザー:8 kHz</string>
<string name="pref_eq_band_7_title">イコライザー:16 kHz</string>
<string name="pref_equalizer_title">イコライザー</string>
<string name="pref_equalizer_summary">オーディオ周波数を調整します。</string>
<string name="pref_equalizer_summary_off">オーディオ周波数の調整は無効です。</string>
<string name="pref_equalizer_reset_title">イコライザーリセット</string>
<string name="pref_preset_selection_title">プリセットを選択</string>
<string name="pref_preset_selection_summary">オーディオプリセットを選択</string>
<string name="pref_preset_none">なし(マニュアル)</string>
<string name="pref_preset_rock">ロック</string>
<string name="pref_preset_pop">ポップ</string>
<string name="pref_preset_jazz">ジャズ</string>
<string name="pref_preset_flat">フラット</string>
<string name="loading">読み込み中…</string>
<string name="media_route_menu_title">キャスト</string>
<string name="pref_visualizer_title">スペクトラムアナライザー</string>
<string name="pref_visualizer_summary">スペクトラムアナライザーを表示します。</string>
</resources>
+62
View File
@@ -16,6 +16,7 @@
<string name="descr_player_playback_button">Afspelen/pauzeren</string>
<string name="descr_player_station_image">Zenderafbeelding</string>
<!-- Dialogs -->
<string name="dialog_add_station_title">Zender Toevoegen</string>
<string name="dialog_edit_station_name">Zendernaam</string>
<string name="dialog_edit_stream_uri">Stream adres</string>
<string name="dialog_error_message_default">Er is een fout opgetreden</string>
@@ -29,6 +30,7 @@
<string name="dialog_generic_button_okay">OK</string>
<string name="dialog_generic_details_button">Toon details</string>
<string name="dialog_opml_import_details_default">Geen details beschikbaar</string>
<string name="dialog_restore_collection_replace_existing">Huidige collectie van radiozenders vervangen met de radiozender uit de back-up?</string>
<string name="dialog_yes_no_message_remove_station">Verwijder deze zender?</string>
<string name="dialog_yes_no_message_update_station_images">Laatste versie van alle zenderafbeeldingen downloaden?</string>
<string name="dialog_yes_no_positive_button_default">Ja</string>
@@ -42,7 +44,10 @@
<string name="notification_stop">Stoppen</string>
<string name="notification_skip_to_previous">Vorige</string>
<string name="notification_skip_to_next">Volgende</string>
<string name="notification_test_content">Dit is een testmelding.</string>
<string name="notification_channel_description">Alle meldingen over de app.</string>
<!-- Onboarding -->
<string name="onboarding_app_description">Dompel jezelf onder in het geluid van je keuze!</string>
<string name="onboarding_app_get_started">Aan de slag</string>
<!-- Player -->
<string name="player_sheet_h2_station_metadata">Nu aan het afspelen</string>
@@ -64,6 +69,11 @@
<string name="pref_general_title">Algemeen</string>
<string name="pref_m3u_export_summary">Sla je radiozenders op in een M3U afspeellijstbestand dat in andere spelers kan worden geïmporteerd.</string>
<string name="pref_m3u_export_title">Exporteer M3U</string>
<string name="pref_pls_export_summary">Sla je radiozenders op in een PLS afspeellijstbestand dat in andere spelers kan worden geïmporteerd.</string>
<string name="pref_pls_export_title">Exporteer PLS</string>
<string name="pref_license_title">Deze applicatie is open source</string>
<string name="pref_license_summary">Gelicentieerd onder de GPLv3 licentie</string>
<string name="pref_links_title">Links</string>
<string name="pref_maintenance_title">Onderhoud</string>
<string name="pref_station_export_summary">Sla de gehele collectie radiozenders, inclusief afbeeldingen, in de apparaatopslag op.</string>
<string name="pref_station_export_title">Exporteer Zender</string>
@@ -76,6 +86,8 @@
<string name="pref_theme_selection_title">App Thema</string>
<string name="pref_update_station_images_summary">Download de laatste versie van alle zenderafbeeldingen.</string>
<string name="pref_update_station_images_title">Update Zenderafbeeldingen</string>
<string name="pref_test_notification_title">Testmelding</string>
<string name="pref_test_notification_summary">Test of het meldingsysteem werkt.</string>
<!-- Sample Text -->
<!-- App Shortcuts -->
<string name="shortcut_last_station_disabled_message">Snelkoppeling voor het afspelen van de laatste zender uitgeschakeld.</string>
@@ -86,6 +98,12 @@
<!-- Toasts -->
<string name="toastmessage_backed_up">back-up is succesvol gemaakt.</string>
<string name="toastmessage_copied_to_clipboard">Gekopieerd naar het klembord.</string>
<string name="toastmessage_connection_failed">De verbinding kon niet tot stand worden gebracht of hersteld.</string>
<string name="toastmessage_error_restart_playback_failed">Kan afspelen niet starten of herstarten.</string>
<string name="toastmessage_preview_playback_started">Voorbeeld-afspelen is gestart.</string>
<string name="toastmessage_preview_playback_failed">Kan voorbeeld niet afspelen.</string>
<string name="toastmessage_save_pls">Radiozenders opslaan als PLS…</string>
<string name="toastmessage_station_duplicate">Deze zender is een duplicaat.</string>
<string name="toastmessage_error_download_error">Download fout</string>
<string name="toastmessage_install_file_helper">Installeer alstublieft een bestandsbeheer applicatie.</string>
<string name="toastmessage_restored">Zenders zijn succesvol hersteld.</string>
@@ -99,4 +117,48 @@
<!-- Snackbars -->
<string name="snackbar_show">Weergeven</string>
<string name="snackbar_update_available">is beschikbaar!</string>
<string name="snackbar_failed_permission_notification">Kan notificatierechtiging niet aanvragen.</string>
<!-- Language Selection -->
<string name="pref_language_selection_title">Taal</string>
<string name="pref_language_selection_summary">Huidige taal</string>
<string name="pref_language_system">🗺️ Systeem</string>
<!-- Settings -->
<string name="pref_update_collection_title">Zenders Bijwerken</string>
<string name="pref_update_collection_summary">Download de laatste versie van alle zenders.</string>
<string name="dialog_yes_no_message_update_collection">Download de laatste versie van alle zenders?</string>
<string name="dialog_yes_no_positive_button_update_collection">Bijwerken</string>
<string name="pref_audio_effects_title">Audio Effecten</string>
<string name="pref_security_title">Securiteit</string>
<string name="pref_security_summary">Meer informatie over de beveiliging van deze toepassing</string>
<string name="pref_bass_boost_title">Bass Boost</string>
<string name="pref_bass_boost_summary">Verhoog de bassversterking.</string>
<string name="pref_reverb_title">Reverb</string>
<string name="pref_reverb_summary">Pas de reverb mix aan.</string>
<string name="pref_drc_title">Dynamisch Bereik Compressie</string>
<string name="pref_drc_summary">Comprimeer het dynamisch bereik voor consistent volume.</string>
<string name="pref_eq_low_title">Equalizer: 31 Hz</string>
<string name="pref_eq_mid_title">Equalizer: 125 Hz</string>
<string name="pref_eq_high_title">Equalizer: 4 kHz</string>
<string name="pref_eq_band_1_title">Equalizer: 62 Hz</string>
<string name="pref_eq_band_2_title">Equalizer: 250 Hz</string>
<string name="pref_eq_band_3_title">Equalizer: 500 Hz</string>
<string name="pref_eq_band_4_title">Equalizer: 1 kHz</string>
<string name="pref_eq_band_5_title">Equalizer: 2 kHz</string>
<string name="pref_eq_band_6_title">Equalizer: 8 kHz</string>
<string name="pref_eq_band_7_title">Equalizer: 16 kHz</string>
<string name="pref_equalizer_title">Equalizer</string>
<string name="pref_equalizer_summary">Pas audio frequenties aan.</string>
<string name="pref_equalizer_summary_off">Het aanpassen van audio frequenties is uitgeschakeld.</string>
<string name="pref_equalizer_reset_title">Reset Equalizer</string>
<string name="pref_preset_selection_title">Selecteer Voorinstelling</string>
<string name="pref_preset_selection_summary">Kies een audio voorinstelling.</string>
<string name="pref_preset_none">Geen (Handmatig)</string>
<string name="pref_preset_rock">Rock</string>
<string name="pref_preset_pop">Pop</string>
<string name="pref_preset_jazz">Jazz</string>
<string name="pref_preset_flat">Plat</string>
<string name="loading">Laden…</string>
<string name="media_route_menu_title">Cast</string>
<string name="pref_visualizer_title">Spectrum Analyser</string>
<string name="pref_visualizer_summary">Toon de Spectrum Analyser.</string>
</resources>
+60
View File
@@ -16,6 +16,7 @@
<string name="descr_player_playback_button">Odtwórz/zatrzymaj</string>
<string name="descr_player_station_image">Ikona stacji</string>
<!-- Dialogs -->
<string name="dialog_add_station_title">Dodaj Stację</string>
<string name="dialog_edit_station_name">Nazwa stacji</string>
<string name="dialog_edit_stream_uri">Adres strumienia</string>
<string name="dialog_error_message_default">Wystąpił błąd</string>
@@ -29,6 +30,7 @@
<string name="dialog_generic_button_okay">OK</string>
<string name="dialog_generic_details_button">Pokaż szczegóły</string>
<string name="dialog_opml_import_details_default">Brak dostępnych szczegółów</string>
<string name="dialog_restore_collection_replace_existing">Zastąpić bieżącą kolekcję stacji radiowych stacją z kopii zapasowej?</string>
<string name="dialog_yes_no_message_remove_station">Usunąć stację?</string>
<string name="dialog_yes_no_message_update_station_images">Pobrać najnowszą wersję obrazów wszystkich stacji?</string>
<string name="dialog_yes_no_positive_button_default">Tak</string>
@@ -42,7 +44,10 @@
<string name="notification_stop">Zatrzymaj</string>
<string name="notification_skip_to_previous">Poprzedni</string>
<string name="notification_skip_to_next">Następny</string>
<string name="notification_test_content">To jest powiadomienie testowe.</string>
<string name="notification_channel_description">Wszystkie powiadomienia o aplikacji.</string>
<!-- Onboarding -->
<string name="onboarding_app_description">Zanurz się w dźwięku swojego wyboru!</string>
<string name="onboarding_app_get_started">Zaczynamy</string>
<!-- Player -->
<string name="player_sheet_h2_station_metadata">Obecnie odtwarzane</string>
@@ -64,6 +69,11 @@
<string name="pref_general_title">Ogólne</string>
<string name="pref_m3u_export_summary">Zapisz swoje stacje radiowe w pliku listy odtwarzania M3U, który można zaimportować do innych odtwarzaczy.</string>
<string name="pref_m3u_export_title">Eksportuj M3U</string>
<string name="pref_pls_export_summary">Zapisz swoje stacje radiowe w pliku listy odtwarzania PLS, który można zaimportować do innych odtwarzaczy.</string>
<string name="pref_pls_export_title">Eksportuj PLS</string>
<string name="pref_license_title">Ta aplikacja jest open source</string>
<string name="pref_license_summary">Licencjonowane na licencji GPLv3</string>
<string name="pref_links_title">Linki</string>
<string name="pref_maintenance_title">Zarządzanie</string>
<string name="pref_station_export_summary">Zapisz do pamięci urządzenia całą kolekcję stacji radiowych, w tym obrazy.</string>
<string name="pref_station_export_title">Eksportuj stacje</string>
@@ -76,6 +86,8 @@
<string name="pref_theme_selection_title">Motyw aplikacji</string>
<string name="pref_update_station_images_summary">Pobierz najnowszą wersję wszystkich obrazów stacji w swojej kolekcji.</string>
<string name="pref_update_station_images_title">Aktualizuj zdjęcia stacji</string>
<string name="pref_test_notification_title">Testowe powiadomienie</string>
<string name="pref_test_notification_summary">Sprawdź, czy system powiadomień działa.</string>
<!-- Sample Text -->
<!-- App Shortcuts -->
<string name="shortcut_last_station_disabled_message">Skrót do odtwarzania ostatniej stacji jest wyłączony.</string>
@@ -86,6 +98,10 @@
<!-- Toasts -->
<string name="toastmessage_backed_up">kopia zapasowa została wykonana.</string>
<string name="toastmessage_copied_to_clipboard">Skopiowano do schowka.</string>
<string name="toastmessage_connection_failed">Nie można nawiązać lub przywrócić połączenia.</string>
<string name="toastmessage_preview_playback_started">Rozpoczęto odtwarzanie podglądu.</string>
<string name="toastmessage_preview_playback_failed">Nie można odtworzyć podglądu.</string>
<string name="toastmessage_save_pls">Zapisywanie stacji radiowych jako PLS…</string>
<string name="toastmessage_error_download_error">Błąd pobierania</string>
<string name="toastmessage_error_restart_playback_failed">Nie można ponownie uruchomić odtwarzania.</string>
<string name="toastmessage_install_file_helper">Proszę zainstalować menedżer plików.</string>
@@ -101,4 +117,48 @@
<!-- Snackbars -->
<string name="snackbar_show">Wyświetl</string>
<string name="snackbar_update_available">jest dostępna!</string>
<string name="snackbar_failed_permission_notification">Nie udało się poprosić o pozwolenie na powiadomienia.</string>
<!-- Language Selection -->
<string name="pref_language_selection_title">Język</string>
<string name="pref_language_selection_summary">Aktualny język</string>
<string name="pref_language_system">🗺️ System</string>
<!-- Settings -->
<string name="pref_update_collection_title">Aktualizacja Stacji</string>
<string name="pref_update_collection_summary">Pobierz najnowszą wersję wszystkich stacji.</string>
<string name="dialog_yes_no_message_update_collection">Pobrać najnowszą wersję wszystkich stacji?</string>
<string name="dialog_yes_no_positive_button_update_collection">Aktualizuj</string>
<string name="pref_audio_effects_title">Efekty Dźwiękowe</string>
<string name="pref_security_title">Bezpieczeństwo</string>
<string name="pref_security_summary">Dowiedz się więcej o bezpieczeństwie tej aplikacji</string>
<string name="pref_bass_boost_title">Wzmocnienie Basów</string>
<string name="pref_bass_boost_summary">Zwiększ wzmocnienie basów.</string>
<string name="pref_reverb_title">Pogłos</string>
<string name="pref_reverb_summary">Dostosuj miks pogłosu.</string>
<string name="pref_drc_title">Kompresja Zakresu Dynamiki</string>
<string name="pref_drc_summary">Kompresuj zakres dynamiki dla spójnej głośności.</string>
<string name="pref_eq_low_title">Korektor: 31 Hz</string>
<string name="pref_eq_mid_title">Korektor: 125 Hz</string>
<string name="pref_eq_high_title">Korektor: 4 kHz</string>
<string name="pref_eq_band_1_title">Korektor: 62 Hz</string>
<string name="pref_eq_band_2_title">Korektor: 250 Hz</string>
<string name="pref_eq_band_3_title">Korektor: 500 Hz</string>
<string name="pref_eq_band_4_title">Korektor: 1 kHz</string>
<string name="pref_eq_band_5_title">Korektor: 2 kHz</string>
<string name="pref_eq_band_6_title">Korektor: 8 kHz</string>
<string name="pref_eq_band_7_title">Korektor: 16 kHz</string>
<string name="pref_equalizer_title">Korektor</string>
<string name="pref_equalizer_summary">Dostosuj częstotliwości audio.</string>
<string name="pref_equalizer_summary_off">Dostosowanie częstotliwości audio jest wyłączone.</string>
<string name="pref_equalizer_reset_title">Resetuj Korektor</string>
<string name="pref_preset_selection_title">Wybierz Preset</string>
<string name="pref_preset_selection_summary">Wybierz preset audio.</string>
<string name="pref_preset_none">Brak (Ręczny)</string>
<string name="pref_preset_rock">Rock</string>
<string name="pref_preset_pop">Pop</string>
<string name="pref_preset_jazz">Jazz</string>
<string name="pref_preset_flat">Płaski</string>
<string name="loading">Ładowanie…</string>
<string name="media_route_menu_title">Przesyłanie</string>
<string name="pref_visualizer_title">Analizator Widma</string>
<string name="pref_visualizer_summary">Pokaż Analizator Widma.</string>
</resources>
+52
View File
@@ -16,6 +16,7 @@
<string name="descr_player_playback_button">Играть/пауза</string>
<string name="descr_player_station_image">Изображение станции</string>
<!-- Dialogs -->
<string name="dialog_add_station_title">Добавить станцию</string>
<string name="dialog_edit_station_name">Имя станции</string>
<string name="dialog_edit_stream_uri">Адрес потока</string>
<string name="dialog_error_message_default">Произошла ошибка</string>
@@ -29,6 +30,7 @@
<string name="dialog_generic_button_okay">ОК</string>
<string name="dialog_generic_details_button">Показать детали</string>
<string name="dialog_opml_import_details_default">Детали недоступны</string>
<string name="dialog_restore_collection_replace_existing">Заменить текущую коллекцию радиостанций радиостанцией из резервной копии?</string>
<string name="dialog_yes_no_message_remove_station">Удалить эту станцию?</string>
<string name="dialog_yes_no_message_update_station_images">Скачать последнюю версию всех изображений станций?</string>
<string name="dialog_yes_no_positive_button_default">Да</string>
@@ -42,7 +44,10 @@
<string name="notification_stop">Остановить</string>
<string name="notification_skip_to_previous">Предыдущий</string>
<string name="notification_skip_to_next">Следующий</string>
<string name="notification_test_content">Это тестовое уведомление.</string>
<string name="notification_channel_description">Все уведомления о приложении.</string>
<!-- Onboarding -->
<string name="onboarding_app_description">Погрузитесь в звук по вашему выбору!</string>
<string name="onboarding_app_get_started">Начать</string>
<!-- Player -->
<string name="player_sheet_h2_station_metadata">Сейчас играет</string>
@@ -81,6 +86,8 @@
<string name="pref_theme_selection_title">Тема приложения</string>
<string name="pref_update_station_images_summary">Скачать последнюю версию всех изображений станций.</string>
<string name="pref_update_station_images_title">Обновить изображения станций</string>
<string name="pref_test_notification_title">Тест уведомления</string>
<string name="pref_test_notification_summary">Проверить, работает ли система уведомлений.</string>
<!-- Sample Text -->
<!-- App Shortcuts -->
<string name="shortcut_last_station_disabled_message">Ярлык для воспроизведения последней станции отключён.</string>
@@ -91,6 +98,7 @@
<!-- Toasts -->
<string name="toastmessage_backed_up">резервная копия создана.</string>
<string name="toastmessage_copied_to_clipboard">Скопировано в буфер обмена.</string>
<string name="toastmessage_connection_failed">Не удалось установить или восстановить соединение.</string>
<string name="toastmessage_error_download_error">Ошибка загрузки</string>
<string name="toastmessage_error_restart_playback_failed">Невозможно запустить или перезапустить воспроизведение.</string>
<string name="toastmessage_install_file_helper">Пожалуйста, установите файловый менеджер.</string>
@@ -109,4 +117,48 @@
<!-- Snackbars -->
<string name="snackbar_show">Показать</string>
<string name="snackbar_update_available">доступно!</string>
<string name="snackbar_failed_permission_notification">Не удалось запросить разрешение на уведомления.</string>
<!-- Language Selection -->
<string name="pref_language_selection_title">Язык</string>
<string name="pref_language_selection_summary">Текущий язык</string>
<string name="pref_language_system">🗺️ Система</string>
<!-- Settings -->
<string name="pref_update_collection_title">Обновление станций</string>
<string name="pref_update_collection_summary">Скачать последнюю версию всех станций.</string>
<string name="dialog_yes_no_message_update_collection">Скачать последнюю версию всех станций?</string>
<string name="dialog_yes_no_positive_button_update_collection">Обновить</string>
<string name="pref_audio_effects_title">Звуковые эффекты</string>
<string name="pref_security_title">Безопасность</string>
<string name="pref_security_summary">Узнать больше о безопасности этого приложения</string>
<string name="pref_bass_boost_title">Усиление басов</string>
<string name="pref_bass_boost_summary">Увеличить усиление басов.</string>
<string name="pref_reverb_title">Реверберация</string>
<string name="pref_reverb_summary">Настроить микс реверберации.</string>
<string name="pref_drc_title">Динамическое сжатие</string>
<string name="pref_drc_summary">Сжать динамический диапазон для постоянной громкости.</string>
<string name="pref_eq_low_title">Эквалайзер: 31 Гц</string>
<string name="pref_eq_mid_title">Эквалайзер: 125 Гц</string>
<string name="pref_eq_high_title">Эквалайзер: 4 кГц</string>
<string name="pref_eq_band_1_title">Эквалайзер: 62 Гц</string>
<string name="pref_eq_band_2_title">Эквалайзер: 250 Гц</string>
<string name="pref_eq_band_3_title">Эквалайзер: 500 Гц</string>
<string name="pref_eq_band_4_title">Эквалайзер: 1 кГц</string>
<string name="pref_eq_band_5_title">Эквалайзер: 2 кГц</string>
<string name="pref_eq_band_6_title">Эквалайзер: 8 кГц</string>
<string name="pref_eq_band_7_title">Эквалайзер: 16 кГц</string>
<string name="pref_equalizer_title">Эквалайзер</string>
<string name="pref_equalizer_summary">Настроить звуковые частоты.</string>
<string name="pref_equalizer_summary_off">Настройка звуковых частот отключена.</string>
<string name="pref_equalizer_reset_title">Сбросить эквалайзер</string>
<string name="pref_preset_selection_title">Выбрать пресет</string>
<string name="pref_preset_selection_summary">Выберите звуковой пресет.</string>
<string name="pref_preset_none">Нет (Ручной)</string>
<string name="pref_preset_rock">Рок</string>
<string name="pref_preset_pop">Поп</string>
<string name="pref_preset_jazz">Джаз</string>
<string name="pref_preset_flat">Плоский</string>
<string name="loading">Загрузка…</string>
<string name="media_route_menu_title">Трансляция</string>
<string name="pref_visualizer_title">Анализатор спектра</string>
<string name="pref_visualizer_summary">Показать анализатор спектра.</string>
</resources>
+51
View File
@@ -16,6 +16,7 @@
<string name="descr_player_playback_button">Відтворити/призупинити</string>
<string name="descr_player_station_image">Зображення станції</string>
<!-- Dialogs -->
<string name="dialog_add_station_title">Додати станцію</string>
<string name="dialog_edit_station_name">Назва станції</string>
<string name="dialog_edit_stream_uri">Адреса трансляції</string>
<string name="dialog_error_message_default">Виникла помилка</string>
@@ -29,6 +30,7 @@
<string name="dialog_generic_button_okay">Добре</string>
<string name="dialog_generic_details_button">Показати подробиці</string>
<string name="dialog_opml_import_details_default">Подробиці недоступні</string>
<string name="dialog_restore_collection_replace_existing">Замінити поточну колекцію радіостанцій радіостанцією з резервної копії?</string>
<string name="dialog_yes_no_message_remove_station">Видалити цю станцію?</string>
<string name="dialog_yes_no_message_update_station_images">Завантажити останню версію всіх зображень станцій?</string>
<string name="dialog_yes_no_positive_button_default">Так</string>
@@ -42,7 +44,10 @@
<string name="notification_stop">Зупинити</string>
<string name="notification_skip_to_previous">Попередня</string>
<string name="notification_skip_to_next">Наступна</string>
<string name="notification_test_content">Це тестове сповіщення.</string>
<string name="notification_channel_description">Усі сповіщення про застосунок.</string>
<!-- Onboarding -->
<string name="onboarding_app_description">Пориньте у звук на ваш вибір!</string>
<string name="onboarding_app_get_started">Початок роботи</string>
<!-- Player -->
<string name="player_sheet_h2_station_metadata">Зараз грає</string>
@@ -81,6 +86,8 @@
<string name="pref_theme_selection_title">Тема застосунку</string>
<string name="pref_update_station_images_summary">Завантажити останню версію всіх зображень станцій.</string>
<string name="pref_update_station_images_title">Оновити зображення станцій</string>
<string name="pref_test_notification_title">Тестове сповіщення</string>
<string name="pref_test_notification_summary">Перевірити, чи працює система сповіщень.</string>
<!-- Sample Text -->
<!-- App Shortcuts -->
<string name="shortcut_last_station_disabled_message">Ярлик для відтворення останньої станції вимкнено.</string>
@@ -110,4 +117,48 @@
<!-- Snackbars -->
<string name="snackbar_show">Показати</string>
<string name="snackbar_update_available">доступне!</string>
<string name="snackbar_failed_permission_notification">Не вдалося запитати дозвіл на сповіщення.</string>
<!-- Language Selection -->
<string name="pref_language_selection_title">Мова</string>
<string name="pref_language_selection_summary">Поточна мова</string>
<string name="pref_language_system">🗺️ Система</string>
<!-- Settings -->
<string name="pref_update_collection_title">Оновлення станцій</string>
<string name="pref_update_collection_summary">Завантажити останню версію всіх станцій.</string>
<string name="dialog_yes_no_message_update_collection">Завантажити останню версію всіх станцій?</string>
<string name="dialog_yes_no_positive_button_update_collection">Оновити</string>
<string name="pref_audio_effects_title">Звукові ефекти</string>
<string name="pref_security_title">Безпека</string>
<string name="pref_security_summary">Дізнайтеся більше про безпеку цього додатку</string>
<string name="pref_bass_boost_title">Підсилення басів</string>
<string name="pref_bass_boost_summary">Збільшити підсилення басів.</string>
<string name="pref_reverb_title">Реверберація</string>
<string name="pref_reverb_summary">Налаштувати мікс реверберації.</string>
<string name="pref_drc_title">Динамічна компресія</string>
<string name="pref_drc_summary">Стиснути динамічний діапазон для стабільної гучності.</string>
<string name="pref_eq_low_title">Еквалайзер: 31 Гц</string>
<string name="pref_eq_mid_title">Еквалайзер: 125 Гц</string>
<string name="pref_eq_high_title">Еквалайзер: 4 кГц</string>
<string name="pref_eq_band_1_title">Еквалайзер: 62 Гц</string>
<string name="pref_eq_band_2_title">Еквалайзер: 250 Гц</string>
<string name="pref_eq_band_3_title">Еквалайзер: 500 Гц</string>
<string name="pref_eq_band_4_title">Еквалайзер: 1 кГц</string>
<string name="pref_eq_band_5_title">Еквалайзер: 2 кГц</string>
<string name="pref_eq_band_6_title">Еквалайзер: 8 кГц</string>
<string name="pref_eq_band_7_title">Еквалайзер: 16 кГц</string>
<string name="pref_equalizer_title">Еквалайзер</string>
<string name="pref_equalizer_summary">Налаштувати звукові частоти.</string>
<string name="pref_equalizer_summary_off">Налаштування звукових частот вимкнено.</string>
<string name="pref_equalizer_reset_title">Скинути еквалайзер</string>
<string name="pref_preset_selection_title">Вибрати пресет</string>
<string name="pref_preset_selection_summary">Виберіть звуковий пресет.</string>
<string name="pref_preset_none">Немає (Ручний)</string>
<string name="pref_preset_rock">Рок</string>
<string name="pref_preset_pop">Поп</string>
<string name="pref_preset_jazz">Джаз</string>
<string name="pref_preset_flat">Плаский</string>
<string name="loading">Завантаження…</string>
<string name="media_route_menu_title">Трансляція</string>
<string name="pref_visualizer_title">Аналізатор спектру</string>
<string name="pref_visualizer_summary">Показати аналізатор спектру.</string>
</resources>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_banner_background">#2C67E6</color>
</resources>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_channel_background">#0A5DBC</color>
</resources>
+24 -2
View File
@@ -2,6 +2,7 @@
<resources>
<!-- App Name -->
<string name="app_version_name" translatable="false">\"Red\"</string>
<string name="app_name" translatable="false">Radio</string>
<!-- Accessibility Descriptions -->
<string name="descr_app_icon">App icon depicting an old radio</string>
@@ -49,6 +50,8 @@
<string name="notification_stop">Stop</string>
<string name="notification_skip_to_previous">Previous</string>
<string name="notification_skip_to_next">Next</string>
<string name="notification_test_content">This is a test notification.</string>
<string name="notification_channel_description">All notifications about the app.</string>
<!-- Onboarding -->
<string name="onboarding_app_description">Immerse yourself in the sound of your choice!</string>
@@ -58,6 +61,21 @@
<string name="player_sheet_h2_station_metadata">Currently playing</string>
<string name="player_sheet_h2_stream_url">Streaming link</string>
<!-- Language Selection -->
<string name="pref_language_selection_title">Language</string>
<string name="pref_language_selection_summary">Current language</string>
<string name="pref_language_system">🗺️ System</string>
<string name="pref_language_en" translatable="false">🇬🇧 English</string>
<string name="pref_language_de" translatable="false">🇩🇪 Deutsch</string>
<string name="pref_language_fr" translatable="false">🇫🇷 Français</string>
<string name="pref_language_ru" translatable="false">🇷🇺 Русский</string>
<string name="pref_language_ja" translatable="false">🇯🇵 日本語</string>
<string name="pref_language_nl" translatable="false">🇳🇱 Nederlands</string>
<string name="pref_language_pl" translatable="false">🇵🇱 Polski</string>
<string name="pref_language_el" translatable="false">🇬🇷 Ελληνικά</string>
<string name="pref_language_da" translatable="false">🇩🇰 Dansk</string>
<!-- Settings -->
<string name="pref_update_collection_title">Update Stations</string>
<string name="pref_update_collection_summary">Download latest version of all station.</string>
@@ -109,6 +127,8 @@
<string name="pref_github_summary" translatable="false">github.com/michatec/Radio</string>
<string name="pref_license_title">This application is open source</string>
<string name="pref_license_summary">Licensed under the GPLv3 License</string>
<string name="pref_security_title">Security</string>
<string name="pref_security_summary">Learn more about the security of this application</string>
<string name="pref_links_title">Links</string>
<string name="pref_m3u_export_summary">Save your radio stations to an M3U playlist file that can be imported into other players.</string>
<string name="pref_m3u_export_title">Export M3U</string>
@@ -126,6 +146,8 @@
<string name="pref_theme_selection_title">App Theme</string>
<string name="pref_update_station_images_summary">Download latest version of all station images.</string>
<string name="pref_update_station_images_title">Update Station Images</string>
<string name="pref_test_notification_title">Test Notification</string>
<string name="pref_test_notification_summary">Test whether the notification system works.</string>
<!-- Sample Text -->
<string name="sample_text_sleep_timer_remaining_time" translatable="false">00:00</string>
@@ -167,11 +189,11 @@
<string name="snackbar_update_available">is available!</string>
<string name="snackbar_url_app_home_page" translatable="false">https://github.com/michatec/Radio/releases/latest</string>
<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>
<string name="snackbar_failed_permission_notification">Failed to request notification permission.</string>
<!-- Extras -->
<string name="loading">Loading...</string>
<string name="loading">Loading</string>
<string name="media_route_menu_title">Cast</string>
<string name="pref_visualizer_title">Spectrum Analyzer</string>
<string name="pref_visualizer_summary">Show the Spectrum Analyzer.</string>
-9
View File
@@ -1,22 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
<locale android:name="en" />
<locale android:name="ar" />
<locale android:name="bg" />
<locale android:name="cs" />
<locale android:name="de" />
<locale android:name="el" />
<locale android:name="fr" />
<locale android:name="hu" />
<locale android:name="it" />
<locale android:name="nl" />
<locale android:name="pl" />
<locale android:name="pt" />
<locale android:name="ru" />
<locale android:name="ro" />
<locale android:name="tr" />
<locale android:name="uk" />
<locale android:name="zh-rCN" />
<locale android:name="da" />
<locale android:name="ja" />
</locale-config>
+8 -8
View File
@@ -1,16 +1,16 @@
[versions]
activityKtx = "1.13.0"
agp = "9.1.0"
agp = "9.2.1"
coreKtx = "1.18.0"
freedroidwarn = "V1.10"
gson = "2.13.2"
kotlin = "2.3.20"
freedroidwarn = "V1.13"
gson = "2.14.0"
kotlin = "2.3.21"
leanback = "1.2.0"
material = "1.13.0"
material = "1.14.0"
material3 = "1.4.0"
media = "1.7.1"
media3 = "1.10.0"
navigation = "2.9.7"
media = "1.8.0"
media3 = "1.10.1"
navigation = "2.9.8"
paletteKtx = "1.0.0"
preferenceKtx = "1.2.1"
volley = "1.2.1"
Binary file not shown.
+3 -1
View File
@@ -1,7 +1,9 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+1 -1
View File
@@ -57,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/b631911858264c0b6e4d6603d677ff5218766cee/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
Vendored
+10 -21
View File
@@ -23,8 +23,8 @@
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Set local scope for the variables, and ensure extensions are enabled
setlocal EnableExtensions
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@@ -51,7 +51,7 @@ echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
"%COMSPEC%" /c exit 1
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
@@ -65,7 +65,7 @@ echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
"%COMSPEC%" /c exit 1
:execute
@rem Setup the command line
@@ -73,21 +73,10 @@ goto fail
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
@rem which allows us to clear the local environment before executing the java command
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
:exitWithErrorLevel
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
"%COMSPEC%" /c exit %ERRORLEVEL%