@@ -73,6 +73,13 @@ public class ExoPlayerService : MediaSessionService
7373 internal static MediaItem ? currentMediaItem ; // Choose a unique ID
7474 public static SongModelView ? CurrentSongContext ; // Choose a unique ID
7575 public static SongModelView ? CurrentSongExposed => CurrentSongContext ;
76+
77+ // Static methods to expose state for notification
78+ internal static bool ShuffleStateInternal = false ;
79+ internal static int RepeatModeInternal = 1 ; // 0=All, 1=Off, 2=One
80+
81+ public static bool GetShuffleState ( ) => ShuffleStateInternal ;
82+ public static int GetRepeatMode ( ) => RepeatModeInternal ;
7683 // --- Service Lifecycle ---
7784 private ExoPlayerServiceBinder ? _binder ;
7885
@@ -321,14 +328,21 @@ public async override void OnCreate()
321328 var shuffleButton = new CommandButton . Builder ( CommandButton . IconUndefined )
322329 . SetDisplayName ( "Shuffle" )
323330 . SetSessionCommand ( new SessionCommand ( ActionShuffle , Bundle . Empty ) )
324- . SetCustomIconResId ( Resource . Drawable . shuffle )
331+ . SetCustomIconResId ( Resource . Drawable . media3_icon_shuffle_off )
325332 . Build ( ) ;
333+
334+ var repeatButton = new CommandButton . Builder ( CommandButton . IconUndefined )
335+ . SetDisplayName ( "Repeat" )
336+ . SetSessionCommand ( new SessionCommand ( ActionRepeat , Bundle . Empty ) )
337+ . SetCustomIconResId ( Resource . Drawable . media3_icon_repeat_off )
338+ . Build ( ) ;
339+
326340 mediaSession = new MediaSession . Builder ( this , notificationPlayer ) !
327341 . SetSessionActivity ( pendingIntent ) !
328342 . SetCallback ( sessionCallback ) !
329343 . SetId ( "Dimmer_MediaSession_Main" ) !
330344
331- . SetCustomLayout ( customLayout : new List < CommandButton > { heartButton , shuffleButton } )
345+ . SetCustomLayout ( customLayout : new List < CommandButton > { heartButton , shuffleButton , repeatButton } )
332346 . Build ( ) ;
333347
334348 _binder = new ExoPlayerServiceBinder ( this ) ;
@@ -406,20 +420,107 @@ public override StartCommandResult OnStartCommand(Intent? intent, StartCommandFl
406420 switch ( action )
407421 {
408422 case ActionFavorite :
409- //MyViewModel.ToggleFavorite(CurrentSongContext);
410- RefreshNotification ( ) ; // Forces the icons to flip
423+ HandleFavoriteAction ( ) ;
411424 break ;
412425 case ActionShuffle :
413- //ToggleShuffle();
426+ HandleShuffleAction ( ) ;
427+ break ;
428+ case ActionRepeat :
429+ HandleRepeatAction ( ) ;
414430 break ;
415431 case ActionLyrics :
416- // Throw your "Not Implemented" lyrics logic here!
432+ HandleLyricsAction ( ) ;
417433 break ;
418434 }
419435
420436
421437 return StartCommandResult . Sticky ;
422438 }
439+
440+ private void HandleFavoriteAction ( )
441+ {
442+ if ( CurrentSongContext != null )
443+ {
444+ // Get the ViewModel and toggle favorite state
445+ var viewModel = MainApplication . ServiceProvider . GetService < BaseViewModel > ( ) ;
446+ if ( viewModel != null )
447+ {
448+ Task . Run ( async ( ) =>
449+ {
450+ try
451+ {
452+ if ( CurrentSongContext . IsFavorite )
453+ {
454+ await viewModel . RemoveSongFromFavorite ( CurrentSongContext ) ;
455+ }
456+ else
457+ {
458+ await viewModel . AddFavoriteRatingToSong ( CurrentSongContext ) ;
459+ }
460+
461+ MainThread . BeginInvokeOnMainThread ( ( ) => RefreshNotification ( ) ) ;
462+ }
463+ catch ( Exception ex )
464+ {
465+ Console . WriteLine ( $ "[ExoPlayerService] Error toggling favorite: { ex . Message } ") ;
466+ }
467+ } ) ;
468+ }
469+ }
470+ }
471+
472+ private void HandleShuffleAction ( )
473+ {
474+ var viewModel = MainApplication . ServiceProvider . GetService < BaseViewModel > ( ) ;
475+ if ( viewModel != null )
476+ {
477+ MainThread . BeginInvokeOnMainThread ( ( ) =>
478+ {
479+ try
480+ {
481+ viewModel . ToggleShuffle ( ) ;
482+ ShuffleStateInternal = viewModel . IsShuffleActive ;
483+ RefreshNotification ( ) ;
484+ }
485+ catch ( Exception ex )
486+ {
487+ Console . WriteLine ( $ "[ExoPlayerService] Error toggling shuffle: { ex . Message } ") ;
488+ }
489+ } ) ;
490+ }
491+ }
492+
493+ private void HandleRepeatAction ( )
494+ {
495+ var viewModel = MainApplication . ServiceProvider . GetService < BaseViewModel > ( ) ;
496+ if ( viewModel != null )
497+ {
498+ MainThread . BeginInvokeOnMainThread ( ( ) =>
499+ {
500+ try
501+ {
502+ viewModel . ToggleRepeatMode ( ) ;
503+ RepeatModeInternal = ( int ) viewModel . CurrentRepeatMode ;
504+ RefreshNotification ( ) ;
505+ }
506+ catch ( Exception ex )
507+ {
508+ Console . WriteLine ( $ "[ExoPlayerService] Error toggling repeat: { ex . Message } ") ;
509+ }
510+ } ) ;
511+ }
512+ }
513+
514+ private void HandleLyricsAction ( )
515+ {
516+ // Open the app to the lyrics view
517+ var intent = new Intent ( this , typeof ( TransitionActivity ) ) ;
518+ intent . SetAction ( Intent . ActionMain ) ;
519+ intent . AddCategory ( Intent . CategoryLauncher ) ;
520+ intent . AddFlags ( ActivityFlags . NewTask ) ;
521+ intent . PutExtra ( "ShowLyrics" , true ) ;
522+ StartActivity ( intent ) ;
523+ }
423524
424525
425526 public override IBinder ? OnBind ( Intent ? intent )
@@ -518,6 +619,15 @@ public Task Prepare(
518619 try
519620 {
520621 CurrentSongContext = song ;
622+
623+ // Sync shuffle and repeat state from ViewModel
624+ var viewModel = MainApplication . ServiceProvider ? . GetService < BaseViewModel > ( ) ;
625+ if ( viewModel != null )
626+ {
627+ ShuffleStateInternal = viewModel . IsShuffleActive ;
628+ RepeatModeInternal = ( int ) viewModel . CurrentRepeatMode ;
629+ }
630+
521631 if ( player is null )
522632 {
523633
@@ -604,13 +714,27 @@ public void UpdateMediaSessionLayout()
604714 . SetCustomIconResId ( isFav ? Resource . Drawable . media3_icon_heart_filled : Resource . Drawable . heart )
605715 . Build ( ) ;
606716
717+ bool isShuffleOn = ShuffleStateInternal ;
607718 var shuffleBtn = new CommandButton . Builder ( CommandButton . IconUndefined )
608719 . SetDisplayName ( "Shuffle" )
609720 . SetSessionCommand ( new SessionCommand ( ActionShuffle , Bundle . Empty ) )
610- . SetCustomIconResId ( Resource . Drawable . shuffle )
721+ . SetCustomIconResId ( isShuffleOn ? Resource . Drawable . media3_icon_shuffle_on : Resource . Drawable . media3_icon_shuffle_off )
722+ . Build ( ) ;
723+
724+ int repeatMode = RepeatModeInternal ;
725+ int repeatIcon = repeatMode switch
726+ {
727+ 2 => Resource . Drawable . media3_icon_repeat_one , // Repeat One
728+ 0 => Resource . Drawable . media3_icon_repeat_all , // Repeat All
729+ _ => Resource . Drawable . media3_icon_repeat_off // Repeat Off
730+ } ;
731+ var repeatBtn = new CommandButton . Builder ( CommandButton . IconUndefined )
732+ . SetDisplayName ( "Repeat" )
733+ . SetSessionCommand ( new SessionCommand ( ActionRepeat , Bundle . Empty ) )
734+ . SetCustomIconResId ( repeatIcon )
611735 . Build ( ) ;
612736
613- var layout = new List < CommandButton > { heartBtn , shuffleBtn } ;
737+ var layout = new List < CommandButton > { heartBtn , shuffleBtn , repeatBtn } ;
614738 mediaSession ? . SetCustomLayout ( ( System . Collections . Generic . IList < CommandButton > ) layout ) ;
615739 }
616740 // --- Player Event Listener ---
@@ -854,7 +978,7 @@ public MediaSession.ConnectionResult OnConnect(
854978 . Add ( SessionCommand . CommandCodeSessionSetRating ) !
855979 . Add ( new SessionCommand ( ExoPlayerService . ActionFavorite , Bundle . Empty ) )
856980 . Add ( new SessionCommand ( ExoPlayerService . ActionShuffle , Bundle . Empty ) ) !
857- . Add ( new SessionCommand ( ExoPlayerService . ActionShuffle , Bundle . Empty ) ) !
981+ . Add ( new SessionCommand ( ExoPlayerService . ActionRepeat , Bundle . Empty ) ) !
858982 . Build ( ) ;
859983
860984
@@ -957,18 +1081,75 @@ public SessionResult OnCustomCommand(MediaSession? session, MediaSession.Control
9571081 var currentSong = ExoPlayerService . CurrentSongContext ;
9581082 if ( currentSong != null )
9591083 {
960- currentSong . IsFavorite = ! currentSong . IsFavorite ;
961- ExoPlayerService . UpdateFavoriteState ( currentSong ) ;
1084+ var viewModel = MainApplication . ServiceProvider . GetService < BaseViewModel > ( ) ;
1085+ if ( viewModel != null )
1086+ {
1087+ Task . Run ( async ( ) =>
1088+ {
1089+ try
1090+ {
1091+ if ( currentSong . IsFavorite )
1092+ {
1093+ await viewModel . RemoveSongFromFavorite ( currentSong ) ;
1094+ }
1095+ else
1096+ {
1097+ await viewModel . AddFavoriteRatingToSong ( currentSong ) ;
1098+ }
1099+
1100+ MainThread . BeginInvokeOnMainThread ( ( ) => service . UpdateMediaSessionLayout ( ) ) ;
1101+ }
1102+ catch ( Exception ex )
1103+ {
1104+ Console . WriteLine ( $ "[ExoPlayerService] Error toggling favorite: { ex . Message } ") ;
1105+ }
1106+ } ) ;
1107+ }
9621108 }
9631109 return ( SessionResult ) SessionResult . ResultSuccess ;
1110+
9641111 case ExoPlayerService . ActionShuffle :
9651112 // Toggle shuffle mode
966- if ( service . player != null )
1113+ var vm = MainApplication . ServiceProvider . GetService < BaseViewModel > ( ) ;
1114+ if ( vm != null )
1115+ {
1116+ MainThread . BeginInvokeOnMainThread ( ( ) =>
1117+ {
1118+ try
1119+ {
1120+ vm . ToggleShuffle ( ) ;
1121+ ExoPlayerService . ShuffleStateInternal = vm . IsShuffleActive ;
1122+ service . UpdateMediaSessionLayout ( ) ;
1123+ }
1124+ catch ( Exception ex )
1125+ {
1126+ Console . WriteLine ( $ "[ExoPlayerService] Error toggling shuffle: { ex . Message } ") ;
1127+ }
1128+ } ) ;
1129+ }
1130+ return ( SessionResult ) SessionResult . ResultSuccess ;
1131+
1132+ case ExoPlayerService . ActionRepeat :
1133+ // Toggle repeat mode
1134+ var vmRepeat = MainApplication . ServiceProvider . GetService < BaseViewModel > ( ) ;
1135+ if ( vmRepeat != null )
9671136 {
968- bool newShuffleState = ! service . player . ShuffleModeEnabled ;
969- service . player . ShuffleModeEnabled = newShuffleState ;
1137+ MainThread . BeginInvokeOnMainThread ( ( ) =>
1138+ {
1139+ try
1140+ {
1141+ vmRepeat . ToggleRepeatMode ( ) ;
1142+ ExoPlayerService . RepeatModeInternal = ( int ) vmRepeat . CurrentRepeatMode ;
1143+ service . UpdateMediaSessionLayout ( ) ;
1144+ }
1145+ catch ( Exception ex )
1146+ {
1147+ Console . WriteLine ( $ "[ExoPlayerService] Error toggling repeat: { ex . Message } ") ;
1148+ }
1149+ } ) ;
9701150 }
9711151 return ( SessionResult ) SessionResult . ResultSuccess ;
1152+
9721153 default :
9731154 return ( SessionResult ) SessionResult . ResultErrorUnknown ;
9741155 }
0 commit comments