'Fast update Android notification

I write simple audio player for Android. I wrote basic functional and now I want create notification for control audio player. I use Android classes Service and Notification and RemoteViews for big and small notifications. This work good, but, when I use fasted flipping for audio tracks my notification don't update text with name audio tracks. I create simple video for demonstration problem:

you-tube.com

I use C# and Xamarin Forms, but it doesn't matter, since the code is very similar to Java. My service:

[Service]
public class PlayerService : Service, IPlayerService
{
    private const string ACTION_SHOW_PLAYER = "music.player.ACTION.SHOW_PLAYER";
    private const string ACTION_PLAY_PAUSE = "music.player.ACTION.PLAY_PAUSE";
    private const string ACTION_PLAY_PREW = "music.player.ACTION.PLAY_PREW";
    private const string ACTION_PLAY_NEXT = "music.player.ACTION.PLAY_NEXT";
    private const string ACTION_STOP_SERVICE = "music.player.ACTION.STOP_SERVICE";
    private const string ANDROID_CHANNEL_ID = "com.companyname.homeremotecontroller";
    private const int NOTIFICATION_ID = 46;

    private RemoteViews _remoteViewsBig = null;
    private RemoteViews _remoteViewsSmall = null;
    private NotificationManager _notificationManager = null;
    private NotificationCompat.Builder _builder = null;

    private static IPlayerService _instance = null;
    private static bool _isActiveBackgroundService = false;
    private static bool _isBeenStartBackgroundService= false;

    public IPlayerService GetInstance()
    {
        return _instance;
    }

    public override IBinder OnBind(Intent intent)
    {
        return null;
    }

    public override void OnCreate()
    {
        base.OnCreate();
        _instance = this;

        MusicPlaylistModel.GetInstance().ChangeSound += () =>
        {
            UpdateSound(MusicPlaylistModel.GetInstance().CurrentSound.SoundName);
        };
        MusicPlaylistModel.GetInstance().MusicPlayer.TimeSound += (start, end) =>
        {
            // TODO: empty
        };
        MusicPlaylistModel.GetInstance().MusicPlayer.TickCurrentSound += (curr) =>
        {
            UpdateProgressSound(curr);
        };
        MusicPlaylistModel.GetInstance().PlayingStatus += (status) =>
        {
            UpdatePlayPauseStatus(status);
        };

        _notificationManager = ((NotificationManager)GetSystemService(NotificationService));
        _notificationManager.CreateNotificationChannel(new NotificationChannel(ANDROID_CHANNEL_ID, "Audio player", NotificationImportance.High));

        Intent notificationIntent = new Intent(MainActivity.Activity, typeof(MainActivity));

        _remoteViewsSmall = GetSmallContentView();
        _remoteViewsBig = GetBigContentView();

        CreateBuilderNotification();
    }

    public override void OnDestroy()
    {
        base.OnDestroy();
    }

    public void CreateBuilderNotification()
    {
        _builder = new NotificationCompat.Builder(MainActivity.Activity, ANDROID_CHANNEL_ID)
                .SetCategory(Notification.ExtraMediaSession)
                .SetCustomBigContentView(_remoteViewsBig)
                .SetCustomContentView(_remoteViewsSmall)
                .SetSmallIcon(Resource.Mipmap.icon)
                .SetOngoing(true)
                .SetSilent(true)
                .SetPriority((int)NotificationPriority.Max)
                .SetVisibility(NotificationCompat.VisibilityPublic)
                .SetAutoCancel(true)
                .SetStyle(new NotificationCompat.DecoratedCustomViewStyle());
    }

    public Notification RecreateNotification()
    {
        return _builder.Build();
    }

    private RemoteViews GetBigContentView()
    {
        RemoteViews mContentViewBig = new RemoteViews(MainActivity.Activity.PackageName, Resource.Layout.player_notify_big);
        Intent closeIntent = new Intent(MainActivity.Activity, typeof(PlayerService));
        closeIntent.SetAction(ACTION_STOP_SERVICE);
        PendingIntent pcloseIntent = PendingIntent.GetService(MainActivity.Activity, 0, closeIntent, 0);
        Intent playPauseIntent = new Intent(MainActivity.Activity, typeof(PlayerService));
        playPauseIntent.SetAction(ACTION_PLAY_PAUSE);
        PendingIntent pPlayPauseIntent = PendingIntent.GetService(MainActivity.Activity, 0, playPauseIntent, 0);
        Intent nextIntent = new Intent(MainActivity.Activity, typeof(PlayerService));
        nextIntent.SetAction(ACTION_PLAY_NEXT);
        PendingIntent pNextIntent = PendingIntent.GetService(MainActivity.Activity, 0, nextIntent, 0);
        Intent prewIntent = new Intent(MainActivity.Activity, typeof(PlayerService));
        prewIntent.SetAction(ACTION_PLAY_PREW);
        PendingIntent pPrewIntent = PendingIntent.GetService(MainActivity.Activity, 0, prewIntent, 0);


        mContentViewBig.SetOnClickPendingIntent(Resource.Id.btnWidgetPlayPauseMusicBig, pPlayPauseIntent);
        mContentViewBig.SetOnClickPendingIntent(Resource.Id.btnWidgetCloseService, pcloseIntent);
        mContentViewBig.SetOnClickPendingIntent(Resource.Id.playNextBig, pNextIntent);
        mContentViewBig.SetOnClickPendingIntent(Resource.Id.playPrevBig, pPrewIntent);

        return mContentViewBig;
    }

    private RemoteViews GetSmallContentView()
    {
        RemoteViews mContentViewSmall = new RemoteViews(MainActivity.Activity.PackageName, Resource.Layout.player_notify_small);
        Intent closeIntent = new Intent(MainActivity.Activity, typeof(PlayerService));
        closeIntent.SetAction(ACTION_STOP_SERVICE);
        PendingIntent pcloseIntent = PendingIntent.GetService(MainActivity.Activity, 0, closeIntent, 0);
        Intent playPauseIntent = new Intent(MainActivity.Activity, typeof(PlayerService));
        playPauseIntent.SetAction(ACTION_PLAY_PAUSE);
        PendingIntent pPlayPauseIntent = PendingIntent.GetService(MainActivity.Activity, 0, playPauseIntent, 0);
        Intent nextIntent = new Intent(MainActivity.Activity, typeof(PlayerService));
        nextIntent.SetAction(ACTION_PLAY_NEXT);
        PendingIntent pNextIntent = PendingIntent.GetService(MainActivity.Activity, 0, nextIntent, 0);
        Intent prewIntent = new Intent(MainActivity.Activity, typeof(PlayerService));
        prewIntent.SetAction(ACTION_PLAY_PREW);
        PendingIntent pPrewIntent = PendingIntent.GetService(MainActivity.Activity, 0, prewIntent, 0);

        mContentViewSmall.SetOnClickPendingIntent(Resource.Id.btnWidgetCloseServiceSmall, pcloseIntent);
        mContentViewSmall.SetOnClickPendingIntent(Resource.Id.btnWidgetPlayPauseMusic, pPlayPauseIntent);
        mContentViewSmall.SetOnClickPendingIntent(Resource.Id.btnWidgetPlayNext, pNextIntent);
        mContentViewSmall.SetOnClickPendingIntent(Resource.Id.btnWidgetPlayPrevious, pPrewIntent);

        return mContentViewSmall;
    }

    public void RedrawNotification()
    {
        if(!_isActiveBackgroundService)
        {
            return;
        }
        var notification = RecreateNotification();
        notification.Flags = NotificationFlags.NoClear | NotificationFlags.OngoingEvent;
        _notificationManager.Notify(NOTIFICATION_ID, notification);
        StartForeground(NOTIFICATION_ID, notification);
    }

    public void Start()
    {
        MainActivity.Activity.StartForegroundService(new Intent(MainActivity.Activity, typeof(PlayerService)));
        _isActiveBackgroundService = true;
        _isBeenStartBackgroundService = true;
    }

    public void Stop()
    {
        MainActivity.Activity.StopService(new Intent(MainActivity.Activity, typeof(PlayerService)));
        _isActiveBackgroundService = false;
    }


    public void UpdateSound(string snd_name)
    {
        _remoteViewsSmall.SetTextViewText(Resource.Id.lblWidgetCurrentMusicName, snd_name);
        _remoteViewsBig.SetTextViewText(Resource.Id.lblWidgetCurrentMusicName, snd_name);
        _builder.SetCustomBigContentView(_remoteViewsBig)
                .SetCustomContentView(_remoteViewsSmall);
        RedrawNotification();
    }

    public void UpdateProgressSound(int currProgress)
    {
        // TODO: empty
    }

    public void UpdatePlayPauseStatus(bool status)
    {
        if(status)
        {
            _remoteViewsSmall.SetImageViewResource(Resource.Id.btnWidgetPlayPauseMusic, Resource.Drawable.play_off);
            _remoteViewsBig.SetImageViewResource(Resource.Id.btnWidgetPlayPauseMusicBig, Resource.Drawable.play_off);
        }
        else
        {
            _remoteViewsSmall.SetImageViewResource(Resource.Id.btnWidgetPlayPauseMusic, Resource.Drawable.pause_off);
            _remoteViewsBig.SetImageViewResource(Resource.Id.btnWidgetPlayPauseMusicBig, Resource.Drawable.pause_off);
        }
        _builder.SetCustomBigContentView(_remoteViewsBig)
                .SetCustomContentView(_remoteViewsSmall);
        RedrawNotification();
    }

    public override StartCommandResult OnStartCommand(Intent? intent, [GeneratedEnum] StartCommandFlags flags, int startId)
    {
        if (_isBeenStartBackgroundService)
        {
            UpdateSound(MusicPlaylistModel.GetInstance().CurrentSound.SoundName);
            UpdatePlayPauseStatus(false);
            RedrawNotification();
            _isBeenStartBackgroundService = false;
        }
        string action = intent.Action;
        if (action != null)
        {
            if (action == ACTION_STOP_SERVICE)
            {
                Stop();
            }
            else
            {
                new Task(async () =>
                {
                    if (action == ACTION_PLAY_PAUSE)
                    {
                        await MusicPlaylistModel.GetInstance().Play();
                        return;
                    }
                    else if (action == ACTION_PLAY_PREW)
                    {
                        await MusicPlaylistModel.GetInstance().Prew();
                        return;
                    }
                    else if (action == ACTION_PLAY_NEXT)
                    {
                        await MusicPlaylistModel.GetInstance().Next();
                        return;
                    }
                }).Start();
            }
        }
        return StartCommandResult.Sticky;
    }
}

To show the notification, the external code calls the Start method, and to hide the notification, the external code calls the Stop method. I check for debugging. Application successful will hit in method UpdateSound, where snd_name - is correct. But text in Notification is not correct.

How fix it?



Solution 1:[1]

In UpdateSound() you call RedrawNotification()

In RedrawNotification() you call RecreateNotification()

In RecreateNotification() you call _builder.Build()

But you never made any changes in _builder.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 David Wasser