From 4b7f60bb8c3e142b2052a815bbdbd487a72c241e Mon Sep 17 00:00:00 2001 From: pca006132 Date: Sun, 2 Nov 2025 16:37:01 +0800 Subject: [PATCH] fix: update MediaItems after network change --- .../tempo/service/MediaService.kt | 51 ++++++++++++++++++- .../tempo/util/MappingUtil.java | 16 ++++++ .../tempo/util/MusicUtil.java | 23 +++++++++ .../tempo/service/MediaService.kt | 49 ++++++++++++++++++ 4 files changed, 137 insertions(+), 2 deletions(-) diff --git a/app/src/degoogled/java/com/cappielloantonio/tempo/service/MediaService.kt b/app/src/degoogled/java/com/cappielloantonio/tempo/service/MediaService.kt index c669a7d3..bb237c62 100644 --- a/app/src/degoogled/java/com/cappielloantonio/tempo/service/MediaService.kt +++ b/app/src/degoogled/java/com/cappielloantonio/tempo/service/MediaService.kt @@ -5,18 +5,20 @@ import android.app.PendingIntent.FLAG_IMMUTABLE import android.app.PendingIntent.FLAG_UPDATE_CURRENT import android.app.TaskStackBuilder import android.content.Intent +import android.net.ConnectivityManager +import android.net.Network +import android.net.NetworkCapabilities import android.os.Binder import android.os.Bundle import android.os.IBinder import android.os.Handler import android.os.Looper +import android.util.Log import androidx.media3.common.* import androidx.media3.common.util.UnstableApi import androidx.media3.exoplayer.DefaultLoadControl import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.source.MediaSource -import androidx.media3.exoplayer.source.TrackGroupArray -import androidx.media3.exoplayer.trackselection.TrackSelectionArray import androidx.media3.session.* import androidx.media3.session.MediaSession.ControllerInfo import com.cappielloantonio.tempo.R @@ -43,6 +45,7 @@ class MediaService : MediaLibraryService() { private lateinit var mediaLibrarySession: MediaLibrarySession private lateinit var shuffleCommands: List private lateinit var repeatCommands: List + private lateinit var networkCallback: CustomNetworkCallback lateinit var equalizerManager: EqualizerManager private var customLayout = ImmutableList.of() @@ -81,6 +84,38 @@ class MediaService : MediaLibraryService() { const val ACTION_BIND_EQUALIZER = "com.cappielloantonio.tempo.service.BIND_EQUALIZER" } + fun updateMediaItems() { + Log.d("MediaService", "update items"); + val n = player.mediaItemCount + val k = player.currentMediaItemIndex + val current = player.currentPosition + val items = (0 .. n-1).map{i -> MappingUtil.mapMediaItem(player.getMediaItemAt(i))} + player.clearMediaItems() + player.setMediaItems(items, k, current) + } + + inner class CustomNetworkCallback : ConnectivityManager.NetworkCallback() { + var wasWifi = false + + init { + val manager = getSystemService(ConnectivityManager::class.java) + val network = manager.activeNetwork + val capabilities = manager.getNetworkCapabilities(network) + if (capabilities != null) + wasWifi = capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) + } + + override fun onCapabilitiesChanged(network : Network, networkCapabilities : NetworkCapabilities) { + val isWifi = networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) + if (isWifi != wasWifi) { + wasWifi = isWifi + widgetUpdateHandler.post(Runnable { + updateMediaItems() + }) + } + } + } + override fun onCreate() { super.onCreate() @@ -90,6 +125,7 @@ class MediaService : MediaLibraryService() { restorePlayerFromQueue() initializePlayerListener() initializeEqualizerManager() + initializeNetworkListener() setPlayer(player) } @@ -99,6 +135,7 @@ class MediaService : MediaLibraryService() { } override fun onDestroy() { + releaseNetworkCallback() equalizerManager.release() stopWidgetUpdates() releasePlayer() @@ -275,6 +312,12 @@ class MediaService : MediaLibraryService() { } } + private fun initializeNetworkListener() { + networkCallback = CustomNetworkCallback() + getSystemService(ConnectivityManager::class.java).registerDefaultNetworkCallback(networkCallback) + updateMediaItems() + } + private fun restorePlayerFromQueue() { if (player.mediaItemCount > 0) return @@ -398,6 +441,10 @@ class MediaService : MediaLibraryService() { mediaLibrarySession.release() } + private fun releaseNetworkCallback() { + getSystemService(ConnectivityManager::class.java).unregisterNetworkCallback(networkCallback) + } + @SuppressLint("PrivateResource") private fun getShuffleCommandButton(sessionCommand: SessionCommand): CommandButton { val isOn = sessionCommand.customAction == CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON diff --git a/app/src/main/java/com/cappielloantonio/tempo/util/MappingUtil.java b/app/src/main/java/com/cappielloantonio/tempo/util/MappingUtil.java index 558aa218..1222804b 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/util/MappingUtil.java +++ b/app/src/main/java/com/cappielloantonio/tempo/util/MappingUtil.java @@ -115,6 +115,22 @@ public class MappingUtil { .build(); } + public static MediaItem mapMediaItem(MediaItem old) { + Uri uri = old.requestMetadata.mediaUri == null ? null : MusicUtil.updateStreamUri(old.requestMetadata.mediaUri); + return new MediaItem.Builder() + .setMediaId(old.mediaId) + .setMediaMetadata(old.mediaMetadata) + .setRequestMetadata( + new MediaItem.RequestMetadata.Builder() + .setMediaUri(uri) + .setExtras(old.requestMetadata.extras) + .build() + ) + .setMimeType(MimeTypes.BASE_TYPE_AUDIO) + .setUri(uri) + .build(); + } + public static List mapDownloads(List items) { ArrayList downloads = new ArrayList<>(); diff --git a/app/src/main/java/com/cappielloantonio/tempo/util/MusicUtil.java b/app/src/main/java/com/cappielloantonio/tempo/util/MusicUtil.java index 696b5b9d..cd2c7a36 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/util/MusicUtil.java +++ b/app/src/main/java/com/cappielloantonio/tempo/util/MusicUtil.java @@ -21,11 +21,16 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; public class MusicUtil { private static final String TAG = "MusicUtil"; + private static final Pattern BITRATE_PATTERN = Pattern.compile("&maxBitRate=\\d+"); + private static final Pattern FORMAT_PATTERN = Pattern.compile("&format=\\w+"); + public static Uri getStreamUri(String id) { Map params = App.getSubsonicClientInstance(false).getParams(); @@ -61,6 +66,24 @@ public class MusicUtil { return Uri.parse(uri.toString()); } + public static Uri updateStreamUri(Uri uri) { + String s = uri.toString(); + Matcher m1 = BITRATE_PATTERN.matcher(s); + s = m1.replaceAll(""); + Matcher m2 = FORMAT_PATTERN.matcher(s); + s = m2.replaceAll(""); + s = s.replace("&estimateContentLength=true", ""); + + if (!Preferences.isServerPrioritized()) + s += "&maxBitRate=" + getBitratePreference(); + if (!Preferences.isServerPrioritized()) + s += "&format=" + getTranscodingFormatPreference(); + if (Preferences.askForEstimateContentLength()) + s += "&estimateContentLength=true"; + + return Uri.parse(s); + } + public static Uri getDownloadUri(String id) { StringBuilder uri = new StringBuilder(); diff --git a/app/src/tempus/java/com/cappielloantonio/tempo/service/MediaService.kt b/app/src/tempus/java/com/cappielloantonio/tempo/service/MediaService.kt index 2ff81ac4..36ea5b26 100644 --- a/app/src/tempus/java/com/cappielloantonio/tempo/service/MediaService.kt +++ b/app/src/tempus/java/com/cappielloantonio/tempo/service/MediaService.kt @@ -4,10 +4,14 @@ import android.app.PendingIntent.FLAG_IMMUTABLE import android.app.PendingIntent.FLAG_UPDATE_CURRENT import android.app.TaskStackBuilder import android.content.Intent +import android.net.ConnectivityManager +import android.net.Network +import android.net.NetworkCapabilities import android.os.Binder import android.os.IBinder import android.os.Handler import android.os.Looper +import android.util.Log import androidx.core.content.ContextCompat import androidx.media3.cast.CastPlayer import androidx.media3.cast.SessionAvailabilityListener @@ -43,6 +47,7 @@ class MediaService : MediaLibraryService(), SessionAvailabilityListener { private lateinit var castPlayer: CastPlayer private lateinit var mediaLibrarySession: MediaLibrarySession private lateinit var librarySessionCallback: MediaLibrarySessionCallback + private lateinit var networkCallback: CustomNetworkCallback lateinit var equalizerManager: EqualizerManager inner class LocalBinder : Binder() { @@ -69,6 +74,38 @@ class MediaService : MediaLibraryService(), SessionAvailabilityListener { } } + fun updateMediaItems() { + Log.d("MediaService", "update items"); + val n = player.mediaItemCount + val k = player.currentMediaItemIndex + val current = player.currentPosition + val items = (0 .. n-1).map{i -> MappingUtil.mapMediaItem(player.getMediaItemAt(i))} + player.clearMediaItems() + player.setMediaItems(items, k, current) + } + + inner class CustomNetworkCallback : ConnectivityManager.NetworkCallback() { + var wasWifi = false + + init { + val manager = getSystemService(ConnectivityManager::class.java) + val network = manager.activeNetwork + val capabilities = manager.getNetworkCapabilities(network) + if (capabilities != null) + wasWifi = capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) + } + + override fun onCapabilitiesChanged(network : Network, networkCapabilities : NetworkCapabilities) { + val isWifi = networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) + if (isWifi != wasWifi) { + wasWifi = isWifi + widgetUpdateHandler.post(Runnable { + updateMediaItems() + }) + } + } + } + override fun onCreate() { super.onCreate() @@ -79,6 +116,7 @@ class MediaService : MediaLibraryService(), SessionAvailabilityListener { initializePlayerListener() initializeCastPlayer() initializeEqualizerManager() + initializeNetworkListener() setPlayer( null, @@ -99,6 +137,7 @@ class MediaService : MediaLibraryService(), SessionAvailabilityListener { } override fun onDestroy() { + releaseNetworkCallback() equalizerManager.release() stopWidgetUpdates() releasePlayer() @@ -178,6 +217,12 @@ class MediaService : MediaLibraryService(), SessionAvailabilityListener { .build() } + private fun initializeNetworkListener() { + networkCallback = CustomNetworkCallback() + getSystemService(ConnectivityManager::class.java).registerDefaultNetworkCallback(networkCallback) + updateMediaItems() + } + private fun restorePlayerFromQueue() { if (player.mediaItemCount > 0) return @@ -374,6 +419,10 @@ class MediaService : MediaLibraryService(), SessionAvailabilityListener { automotiveRepository.deleteMetadata() } + private fun releaseNetworkCallback() { + getSystemService(ConnectivityManager::class.java).unregisterNetworkCallback(networkCallback) + } + private fun getRenderersFactory() = DownloadUtil.buildRenderersFactory(this, false) override fun onCastSessionAvailable() {