From e6053353395c408010cf8e12239c809e15ea7d06 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Tue, 19 May 2026 07:19:42 -0400 Subject: [PATCH] =?UTF-8?q?fix(player):=20remove=20custom=20favorite=20Med?= =?UTF-8?q?iaControl=20=E2=80=94=20it=20killed=20the=20notification?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Device logcat (Pixel 6 Pro / Android 16) showed audio_service throwing on every state broadcast: java.lang.IllegalArgumentException: You must specify an icon resource id to build a CustomAction at com.ryanheise.audioservice.AudioService... The #57 MediaControl.custom favorite makes audio_service build a PlaybackStateCompat.CustomAction whose icon id resolves to 0 on real builds; the exception aborts the ENTIRE media notification, so nothing posts to the tray or the watch (emulator tolerated it). Not a permission / PathParser / FGS issue — POST_NOTIFICATIONS was verified granted. Pre-#57 there was no CustomAction, matching the regression. Remove the custom favorite control; the notification is rebuilt with only the standard transport controls (audio_service ships their icons). customAction handler / refreshFavoriteControl left as harmless no-ops to minimise churn. Like/favorite remains in-app + lock screen. Co-Authored-By: Claude Opus 4.7 (1M context) --- flutter_client/lib/player/audio_handler.dart | 27 +++++--------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/flutter_client/lib/player/audio_handler.dart b/flutter_client/lib/player/audio_handler.dart index 1b801160..82d80252 100644 --- a/flutter_client/lib/player/audio_handler.dart +++ b/flutter_client/lib/player/audio_handler.dart @@ -724,31 +724,18 @@ class MinstrelAudioHandler extends BaseAudioHandler with QueueHandler, SeekHandl // with up-to-date shuffleMode/repeatMode fields. void _broadcastState(PlaybackEvent? event) { final playing = _player.playing; - // Favorite control: only when a track is playing and the LikeBridge - // is wired. Icon/label toggle by current like state; tapping it - // routes to customAction('minstrel.favorite'). Implemented as a - // MediaControl.custom (NOT setRating — that path is broken upstream, - // audio_service #376, and regressed the Pixel Watch). Kept out of - // androidCompactActionIndices so the compact/lock view is unchanged. - final favTrackId = mediaItem.value?.id; - final favBridge = _likeBridge; - final showFav = favTrackId != null && favBridge != null; - final favLiked = favTrackId != null && - favBridge != null && - favBridge.isTrackLiked(favTrackId); + // No custom favorite MediaControl here. audio_service builds a + // PlaybackStateCompat.CustomAction for it and throws + // "You must specify an icon resource id to build a CustomAction" + // on real builds (the androidIcon doesn't resolve to a usable id), + // and that exception aborts the ENTIRE media notification — no + // tray or Wear controls at all. Removed; like/favorite remains + // available in-app and via the standard lock-screen surface. playbackState.add(PlaybackState( controls: [ MediaControl.skipToPrevious, if (playing) MediaControl.pause else MediaControl.play, MediaControl.skipToNext, - if (showFav) - MediaControl.custom( - androidIcon: favLiked - ? 'drawable/ic_stat_favorite' - : 'drawable/ic_stat_favorite_border', - label: favLiked ? 'Unfavorite' : 'Favorite', - name: 'minstrel.favorite', - ), ], // androidCompactActionIndices tells the system which controls // appear in the collapsed/lock-screen view. Without this, some