Search code examples
flutternotification-listenernotificationlistenerservice

Change song progress in notification


I am using the flutter_notification_listener library to get info related to the currently playing song (on Spotify). Then, when the user presses a button, the song either pauses/resumes. This works perfectly! More than this I was curious to know if there was any method to change the progress of the song using a progress bar in the app? Like: when the user drags the progress bar the song's progress is also changed? This is the code I'm using now:

late NotificationEvent audioEvent;


  void onData(NotificationEvent event) {
    setState(() {
      audioEvent = event;
    });
  }

  Future<void> initPlatformState() async {
    NotificationsListener.initialize();
    NotificationsListener.receivePort?.listen((evt) => onData(evt));
  }

  void startListening() async {
    var hasPermission = await NotificationsListener.hasPermission;
    if (!hasPermission!) {
      NotificationsListener.openPermissionSettings();
      return;
    }

    var isR = await NotificationsListener.isRunning;

    if (!isR!) {
      await NotificationsListener.startService();
    }
   }

  void playPause() {
    audioEvent.actions?[2].tap();   // It works
  }

  @override
  void initState() {
    initPlatformState();
    Future.delayed(const Duration(seconds: 2), () => startListening());
    super.initState();
  }

Button:

TextButton(onPressed: playPause, child: const Text('Play/Pause'))

Any help is highly appreciated ! Thank you


Solution

  • Finally got the solution. By default there is no library/plugin, that I know of, can achieve this. We have to take advantage of the platform channels in flutter and use native java.
    I created a new MethodChannel and invoked a method to communicate with the java code:

    static const platform =
          MethodChannel('com.example.foobar/playbackstatus');
    
      Future<void> _setPlayBackDur() async {
        try {
          final int result = await platform.invokeMethod('setPlayBackDur');
        } on PlatformException catch (e) {
          
        }
      }
    

    Next follow the steps in the platform channels docs.
    MainActivity.java :

    package com.example.audionotification;
    
    import android.content.ComponentName;
    import android.content.Context;
    import android.content.ContextWrapper;
    import android.content.Intent;
    import android.content.IntentFilter;
    import android.media.session.MediaController;
    import android.media.session.MediaSessionManager;
    import android.os.BatteryManager;
    import android.os.Build.VERSION;
    import android.os.Build.VERSION_CODES;
    
    import androidx.annotation.NonNull;
    import androidx.core.app.NotificationManagerCompat;
    
    import java.util.List;
    import java.util.Set;
    
    import io.flutter.embedding.android.FlutterActivity;
    import io.flutter.embedding.engine.FlutterEngine;
    import io.flutter.plugin.common.MethodChannel;
    
    public class MainActivity extends FlutterActivity {
        private static final String CHANNEL = "com.example.foobar/playbackstatus";
    
        @Override
        public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
            super.configureFlutterEngine(flutterEngine);
            new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
                    .setMethodCallHandler(
                            (call, result) -> {
                                // This method is invoked on the main thread.
                                if (call.method.equals("setPlayBackDur")) {
                                    
    
    
                                    if (!permissionGrantred()) {
                                        Intent intent = new Intent(
                                                "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
                                        startActivity(intent);
                                        result.error("UNAVAILABLE", "...", null);
                                    } else {
                                        MediaSessionManager msm = (MediaSessionManager) getSystemService(Context.MEDIA_SESSION_SERVICE);
                                        ComponentName cn = new ComponentName(this, MyNotificationService.class);
                                        List<MediaController> list = msm.getActiveSessions(cn);
                                        for (MediaController mc : list) {
                                            if (mc.getPackageName().equals("com.spotify.music")) {
                                                mc.getTransportControls().seekTo(136901);  //ms to seekto
                                                result.success(136901);
                                            }
                                        }
                                    }
                                } else {
                                    result.notImplemented();
                                }
                            }
    
                    );
        }
    
        private boolean permissionGrantred() {
            Set<String> sets = NotificationManagerCompat.getEnabledListenerPackages(this);
            if (sets != null && sets.contains(getPackageName())) {
                return true;
            } else {
                return false;
            }
        }
    
    //not included
            private int getBatteryLevel() {
                int batteryLevel = -1;
                if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
                    BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE);
                    batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
                } else {
                    Intent intent = new ContextWrapper(getApplicationContext()).
                            registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
                    batteryLevel = (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) /
                            intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
                }
        
                return batteryLevel;
            }
        }
    

    MyNotificationService.java:

    package com.example.audionotification;
    
    import android.service.notification.NotificationListenerService;
    import android.service.notification.StatusBarNotification;
    
    
    public class MyNotificationService extends NotificationListenerService {
        @Override
        public void onNotificationRemoved(StatusBarNotification sbn) {
            super.onNotificationRemoved(sbn);
        }
    
        @Override
        public void onNotificationPosted(StatusBarNotification sbn) {
            super.onNotificationPosted(sbn);
        }
    }
    

    Hope it helps! (Thx to https://stackoverflow.com/a/66795488/14326852)