2

The Search :

For days, I've been looking at how to control MediaPlayer which is running inside a Service. Basically, media controls in a ForegroundService/NotificationCompat

And I read an answer, https://stackoverflow.com/questions/11270898/how-to-execute-a-method-by-clicking-a-notification which says use an IntentService inside the same class.

What I did :

When Service starts, MediaPlayer would be playing the song already. So means MediaPlayer.isPlaying() will return true

Then I implemented IntentService inside my music playing service, also registered that IntentService in AndroidManifest.xml

I created two Actions, Pause and Resume. So by default the notification will have Pause Button as Action and when user clicks on it, it will send Intent(MusicService.this, MusicNotificationService.class).setAction("test.pause") using PendingIntent

Problem :

I am able to play songs continuously without any problem. Also, the Pause Button is displaying.

But, I am receiving following error whenever I click on Pause Button.

My Code :

public class MusicService extends Service {

    private ArrayList<String> nameList, artistList, urlList;
    private int position;

    private MediaPlayer mediaPlayer;

    private NotificationCompat.Builder builder;
    private NotificationManager manager;

    private PendingIntent pendingMain;

    private NotificationCompat.Action actionPause, actionResume;
    private static final String channelId = "Test", subtext = "Now Playing";
    private static final String[] actions = {"test.pause", "test.resume"},
                                  actionTitles = {"pause", "resume"};

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        nameList = intent.getStringArrayListExtra("names");
        artistList = intent.getStringArrayListExtra("artists");
        urlList = intent.getStringArrayListExtra("urls");
        position = intent.getIntExtra("position", 0);

        builder = new NotificationCompat.Builder(this, channelId)
                .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
                .setSmallIcon(R.drawable.ic_music_note)
                .setContentTitle(nameList.get(position))
                .setContentText(artistList.get(position))
                .setSubText(subtext)
                .setContentIntent(pendingMain)
                .addAction(actionPause)
                .setStyle(new androidx.media.app.NotificationCompat.MediaStyle()
                            .setShowActionsInCompactView(0)
                            .setShowCancelButton(true));

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            createNotificationChannel();
            startForeground(99, builder.build());
        } else {
            startForeground(99, builder.build());
        }

        play(urlList.get(position));

        return START_NOT_STICKY;
    }

    @Override
    public void onCreate() {
        super.onCreate();

        manager = getSystemService(NotificationManager.class);

        Intent mainIntent = new Intent(this, MainActivity.class);
        pendingMain = PendingIntent.getActivity(this, 3, mainIntent, PendingIntent.FLAG_UPDATE_CURRENT);

        Intent pause = new Intent(this, MusicNotificationService.class).setAction(actions[0]);
        PendingIntent pendingPause = PendingIntent.getService(this, 4, pause, PendingIntent.FLAG_ONE_SHOT);

        Intent resume = new Intent(this, MusicNotificationService.class).setAction(actions[1]);
        PendingIntent pendingResume = PendingIntent.getService(this, 5, resume, PendingIntent.FLAG_ONE_SHOT);

        actionPause = new NotificationCompat.Action(R.drawable.ic_pause, actionTitles[0], pendingPause);
        actionResume = new NotificationCompat.Action(R.drawable.ic_play, actionTitles[1], pendingResume);

        if (mediaPlayer == null) {
            mediaPlayer = new MediaPlayer();
            mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
        }

        mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared(MediaPlayer mp) {
                mp.start();
            }
        });

        mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                if (position + 1 < urlList.size()) {
                    position += 1;
                    builder.setContentTitle(nameList.get(position));
                    builder.setContentText(artistList.get(position));
                    manager.notify(99, builder.build());
                    play(urlList.get(position));
                } else {
                    mp.stop();
                }
            }
        });
    }

    public class MusicNotificationService extends IntentService {

        public MusicNotificationService() {
            super(MusicNotificationService.class.getSimpleName());
        }

        @SuppressLint("RestrictedApi")
        @Override
        protected void onHandleIntent(@Nullable Intent intent) {
            String action = Objects.requireNonNull(intent).getAction();
            if (action != null) {
                if (mediaPlayer.isPlaying()) {
                    if (action.equalsIgnoreCase(actions[0])) {
                        mediaPlayer.pause();
                        builder.mActions.set(0, actionResume);
                        manager.notify(99, builder.build());
                    }
                } else if (action.equalsIgnoreCase(actions[1])) {
                    mediaPlayer.start();
                    builder.mActions.set(0, actionPause);
                    manager.notify(99, builder.build());
                }
            }
        }
    }
}

Error :

E/AndroidRuntime: FATAL EXCEPTION: main
Process: test.music, PID: 20335
java.lang.RuntimeException: Unable to instantiate service test.music.MusicService$MusicNotificationService:
java.lang.InstantiationException: java.lang.Class<test.music.MusicService$MusicNotificationService>
has no zero argument constructor

    at android.app.ActivityThread.handleCreateService(ActivityThread.java:3974)
    at android.app.ActivityThread.access$1600(ActivityThread.java:220)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1899)
    at android.os.Handler.dispatchMessage(Handler.java:107)
    at android.os.Looper.loop(Looper.java:224)
    at android.app.ActivityThread.main(ActivityThread.java:7520)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
 Caused by: java.lang.InstantiationException: java.lang.Class<test.music.MusicService$MusicNotificationService>
            has no zero argument constructor
    at java.lang.Class.newInstance(Native Method)
    at android.app.AppComponentFactory.instantiateService(AppComponentFactory.java:129)
    at androidx.core.app.CoreComponentFactory.instantiateService(CoreComponentFactory.java:66)
    at android.app.ActivityThread.handleCreateService(ActivityThread.java:3969)
    at android.app.ActivityThread.access$1600(ActivityThread.java:220) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1899) 
    at android.os.Handler.dispatchMessage(Handler.java:107) 
    at android.os.Looper.loop(Looper.java:224) 
    at android.app.ActivityThread.main(ActivityThread.java:7520) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)

What I want to achieve :

For now, I want to be able to Pause and Resume music.

Detained Developer
  • 1,134
  • 2
  • 13
  • 33
  • 2
    Pretty sure it's because `MusicNotificationService` is not a static inner class. Also pretty it's pretty arbitrary that he's using a static inner class there and could just be a public class in it's own file. – Steve M Apr 26 '20 at 13:13
  • @SteveM creating a `static class` stops me from calling a method or variables inside `MusicService` class. What should I do? – Detained Developer Apr 26 '20 at 13:36
  • 1
    There's no need for the separate `IntentSerice`. Simpy target your `MusicService.class` in the `Intent`s, and differentiate what you should be doing in `onStartCommand()` by the action on the `Intent` passed into that method. – Mike M. Apr 27 '20 at 05:14
  • Thank you @MikeM. @SteveM for suggestions ! I was able to solve this using `BroadcastReceiver`, I'll be uploading the code soon. – Detained Developer Apr 27 '20 at 05:20

0 Answers0