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:
- Using other views in the layout as the anchor view (even tho the doc says you can use the Activity's main view)
- Putting the MediaController in a fragment, and using getActivity() as the context, as shown here
- Putting the MediaController in the xml layout instead of instantiating it programatically.
- Setting a VideoView as the anchor view (some people say it only works with a VideoView).
- 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.