mirror of
https://github.com/Michatec/Radio.git
synced 2026-05-31 00:42:40 +02:00
Compare commits
9 Commits
b537df898b
...
f7ddc30127
| Author | SHA1 | Date | |
|---|---|---|---|
| f7ddc30127 | |||
| bc0eb60e04 | |||
| dcae7bafb5 | |||
| cf3fd0bc56 | |||
| 7d6b0fe136 | |||
| 0faeea7631 | |||
| c45adf07c8 | |||
| acd1b067b3 | |||
| e3a325f568 |
@@ -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 {
|
||||
|
||||
@@ -48,6 +48,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 +137,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 +150,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.michatec.radio.helpers
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.util.Log
|
||||
import com.michatec.radio.R
|
||||
import java.util.Locale
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -171,7 +171,7 @@ class SearchResultAdapter(
|
||||
context: Context,
|
||||
enableFloatOutput: Boolean,
|
||||
enableAudioTrackPlaybackParams: Boolean
|
||||
): AudioSink? {
|
||||
): AudioSink {
|
||||
return DefaultAudioSink.Builder(context)
|
||||
.setAudioProcessors(arrayOf(nativeAudioProcessor))
|
||||
.build()
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
[versions]
|
||||
activityKtx = "1.13.0"
|
||||
agp = "9.2.0"
|
||||
agp = "9.2.1"
|
||||
coreKtx = "1.18.0"
|
||||
freedroidwarn = "V1.12"
|
||||
freedroidwarn = "V1.13"
|
||||
gson = "2.14.0"
|
||||
kotlin = "2.3.21"
|
||||
leanback = "1.2.0"
|
||||
material = "1.13.0"
|
||||
material3 = "1.4.0"
|
||||
media = "1.7.1"
|
||||
media = "1.8.0"
|
||||
media3 = "1.10.0"
|
||||
navigation = "2.9.8"
|
||||
paletteKtx = "1.0.0"
|
||||
|
||||
Reference in New Issue
Block a user