4

I'm creating a music streaming app. As per android's MediaPlayer guide, I'm controlling the MediaPlayer from a Service. This all works fine and now I'm trying to add a MediaController to control playback. To do so, I'm having my Service implement MediaController.MediaPlayerControl, and having my Activity bind to my Service, and then instantiating the MediaController from the Activity with the Service context from the ServiceConnection.

Player.java

public class Player extends Activity implements OnClickListener, OnItemClickListener, MediaController.MediaPlayerControl {
    private MediaController mediaController;
    private ServiceConnection mConnection = new ServiceConnection() {    
        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
            // We've bound to LocalService, cast the IBinder and get LocalService instance
            LocalBinder binder = (LocalBinder) service;
            mService = binder.getService();
            mBound = true;
            showMediaController();
        }    
        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mBound = false;
        }
    };
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_player);

        startService(
            new Intent(this, PlayerService.class)
            .setAction("com.limastreamer.action.NEXTSHOW"));

        bindService(
            new Intent(this, PlayerService.class),
            mConnection,
            Context.BIND_AUTO_CREATE);
    }

    public void showMediaController() {
        if (mBound) {
            mediaController = new MediaController(this);
            mediaController.setAnchorView(
                findViewById(R.id.player)
                );
            mediaController.setMediaPlayer(mService);
            mediaController.setEnabled(true);
            mediaController.show(0);
        }
    }
}

PlayerService.java

public class PlayerService extends Service implements MediaController.MediaPlayerControl {
    private MediaPlayer mMediaPlayer;

    public class LocalBinder extends Binder {
        PlayerService getService() {
            return PlayerService.this;
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        String action = intent.getAction();
        if (action.equals("com.limastreamer.action.NEXTSHOW")) {
            if (mMediaPlayer == null)
            {
                mMediaPlayer = new MediaPlayer();
                mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
                mMediaPlayer.setLooping(false);         
            }
            try
            {
                mMediaPlayer.reset();
                mMediaPlayer.setDataSource(url);
                mMediaPlayer.prepareAsync(); // prepare async to not block main thread
            }
            catch (Exception ex)
            {
                Toast.makeText(getApplicationContext(), "Failed to prepare MediaPlayer", Toast.LENGTH_SHORT).show();
            }
        }
    }

    @Override
    public boolean canPause() {
        return true;
    }

    @Override
    public boolean canSeekBackward() {
        return true;
    }

    @Override
    public boolean canSeekForward() {
        return true;
    }

    @Override
    public int getBufferPercentage() {
        return 0;
        }

    @Override
    public int getCurrentPosition() {
        if (mMediaPlayer != null && mMediaPlayer.isPlaying())
            return mMediaPlayer.getCurrentPosition();
        else
            return 0;
    }

    @Override
    public int getDuration() {
        if (mMediaPlayer != null && mMediaPlayer.isPlaying())
            return mMediaPlayer.getDuration();
        else
            return 0;
    }

    @Override
    public boolean isPlaying() {
        if (mMediaPlayer != null)
            return mMediaPlayer.isPlaying();
        else
            return false;
    }

    @Override
    public void pause() {
        if (mMediaPlayer != null)
            mMediaPlayer.pause();
    }

    @Override
    public void seekTo(int msec) {
        if (mMediaPlayer != null)
            mMediaPlayer.seekTo(msec);
    }

    @Override
    public void start() {
        if (mMediaPlayer != null)
            mMediaPlayer.start();       
    }
}

R.id.player refers to the root element of my xml layout.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/player"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".Player" >

On calling mediaController.show(); the app bombs out with the exception: Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?

From looking at other questions on SO (for example), it seems that this is caused by using the wrong context here: mediaController = new MediaController(this);, ie using something other than the Activity context. But as far as I can tell, I am using the Activity context.

I've tried:

  1. Using other views in the layout as the anchor view (even tho the doc says you can use the Activity's main view)
  2. Putting the MediaController in a fragment, and using getActivity() as the context, as shown here
  3. Putting the MediaController in the xml layout instead of instantiating it programatically.
  4. Setting a VideoView as the anchor view (some people say it only works with a VideoView).
  5. Creating a new class that extends VideoView and implements MediaPlayerControl, and instantiating the MediaController in that class, using the saved context that was passed to the class when it was initialized as the context, and this as the anchor view.
Community
  • 1
  • 1

1 Answers1

1

Your activity should implement MediaPlayer.OnPreparedListener and set onPreparedListener of mediaPlayer of your service to the player activity.

public class MyMediaPlayer extends Activity implements 
MediaController.MediaPlayerControl,MediaPlayer.OnPreparedListener {
...
public void onCreate(Bundle savedInstanceState) {
...
    //this mediaPlayer is the reference of your media player inside your service
    mediaPlayer.setOnPreparedListener(this);
...
}
...
}

also you have to start your service a little after you created your activity

 Intent in=new Intent(MainPlayer.this,MyMediaPlayer.class);
        startActivity(in);
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //START YOUR SERVICE TO PREPARE YOUR PLAYER

this works for me.

Moein
  • 21
  • 7