I'm implementing Android Auto support for my app, but it is not working properly. I've followed Google's documentation and some tutorials, but the audio is not working. The controls, album art, artist name, etc., all appear just fine.
Strange behaviour: After running Spotify app through the Auto emulator and playing a song, if I go to my app and Try to play the audio, it works!
Here's the service class I've implemented:
/**
* Created by FelipeRRM on 8/13/2016.
*/
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class AutoMediaBrowserService extends MediaBrowserServiceCompat {
private static final String CURRENT_MEDIA_POSITION = "media_position_key";
private static final int PLAY = 1;
private static final int PAUSE = 2;
private static final int BUFFERING = 3;
private static final int CONNECTING = 4;
private static final int STOPPED = 5;
MediaPlayer mediaPlayer;
private static final String MY_MEDIA_ROOT_ID = "meuiddaraiz";
MediaSessionCompat mSession;
#Override
public void onCreate() {
super.onCreate();
mSession = new MediaSessionCompat(this, "session tag");
setSessionToken(mSession.getSessionToken());
// Set a callback object to handle play control requests, which
// implements MediaSession.Callback
mSession.setCallback(new MediaSessionCompat.Callback() {
#Override
public void onPlay() {
super.onPlay();
playMedia( PreferenceManager.getDefaultSharedPreferences( getApplicationContext() ).getInt( CURRENT_MEDIA_POSITION, 0 ), null );
}
//This is called when the pause button is pressed, or when onPlayFromMediaId is called in
//order to pause any currently playing media
#Override
public void onPause() {
super.onPause();
setMediaPlaybackState(PAUSE);
pauseMedia();
}
#Override
public void onStop() {
super.onStop();
setMediaPlaybackState(STOPPED);
if( mediaPlayer != null ) {
pauseMedia();
mediaPlayer.release();
PreferenceManager.getDefaultSharedPreferences(AutoMediaBrowserService.this).edit().putInt( CURRENT_MEDIA_POSITION,
0 ).commit();
}
}
#Override
public void onPlayFromMediaId(String mediaId, Bundle extras) {
super.onPlayFromMediaId(mediaId, extras);
mSession.setMetadata(new MediaMetadataCompat.Builder()
.putText(MediaMetadataCompat.METADATA_KEY_TITLE, "primeira musica")
.putText(MediaMetadataCompat.METADATA_KEY_ARTIST, "Joãozinho das Rezas")
.putText(MediaMetadataCompat.METADATA_KEY_GENRE, "Gospel")
.putText(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, "http://70.38.6.72/~vivafe/web/wp-content/uploads/2016/08/01.jpg")
.build()
);
playMedia(0, mediaId);
}
#Override
public void onPlayFromSearch(String query, Bundle extras) {
super.onPlayFromSearch(query, extras);
}
});
mSession.setActive(true);
}
private void setMediaPlaybackState( int state ) {
PlaybackStateCompat playbackState = null;
switch (state) {
case PLAY:
playbackState = new PlaybackStateCompat.Builder()
.setActions( PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS )
.setState( PlaybackStateCompat.STATE_PLAYING, 0, 1 )
.build();
break;
case PAUSE:
playbackState = new PlaybackStateCompat.Builder()
.setActions( PlaybackStateCompat.ACTION_PLAY_PAUSE )
.setState(PlaybackStateCompat.STATE_PAUSED, 0, 1)
.build();
break;
case BUFFERING:
playbackState = new PlaybackStateCompat.Builder()
.setActions( PlaybackStateCompat.ACTION_STOP )
.setState(PlaybackStateCompat.STATE_BUFFERING, 0, 1)
.build();
break;
case CONNECTING:
playbackState = new PlaybackStateCompat.Builder()
.setActions( PlaybackStateCompat.ACTION_STOP )
.setState(PlaybackStateCompat.STATE_CONNECTING, 0, 1)
.build();
break;
case STOPPED:
playbackState = new PlaybackStateCompat.Builder()
.setActions( PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID )
.setState(PlaybackStateCompat.STATE_STOPPED, 0, 1)
.build();
break;
}
mSession.setPlaybackState( playbackState );
}
private void playMedia(final int position, String id ) {
setMediaPlaybackState(BUFFERING);
if( mediaPlayer != null )
try {
mediaPlayer.reset();
}
catch (Exception e){
mediaPlayer = new MediaPlayer();
e.printStackTrace();
}
else
mediaPlayer = new MediaPlayer();
try {
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource("http://70.38.6.72/~vivafe/web/wp-content/uploads/2016/08/Mateus-01.mp3");
mediaPlayer.prepareAsync();
}
catch (Exception e){
e.printStackTrace();
}
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.start();
if( position > 0 )
mediaPlayer.seekTo( position );
setMediaPlaybackState(PLAY);
}
});
mediaPlayer.setOnInfoListener(new MediaPlayer.OnInfoListener() {
#Override
public boolean onInfo(MediaPlayer mediaPlayer, int state, int extra) {
switch (state){
case MediaPlayer.MEDIA_INFO_BUFFERING_START:{
Log.d("MediaPlayer","StartBuffer");
setMediaPlaybackState(BUFFERING);
break;
}
case MediaPlayer.MEDIA_INFO_BUFFERING_END:{
Log.d("MediaPlayer","EndBuffer");
setMediaPlaybackState(PLAY);
}
}
return true;
}
});
mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
#Override
public boolean onError(MediaPlayer mediaPlayer, int state, int extra) {
Log.e("MediaPlayer Error", String.valueOf(state));
return false;
}
});
}
private void pauseMedia() {
if( mediaPlayer != null ) {
mediaPlayer.pause();
PreferenceManager.getDefaultSharedPreferences( this ).edit().putInt( CURRENT_MEDIA_POSITION,
mediaPlayer.getCurrentPosition() ).commit();
}
}
#Nullable
#Override
public BrowserRoot onGetRoot(String s, int i, Bundle bundle) {
return new BrowserRoot(MY_MEDIA_ROOT_ID, null);
}
#Override
public void onLoadChildren(#NonNull String parentId, #NonNull Result<List<MediaBrowserCompat.MediaItem>> result) {
List<MediaBrowserCompat.MediaItem> mediaItems = new ArrayList<>();
// Check if this is the root menu:
if (parentId.equals(MY_MEDIA_ROOT_ID)) {
mediaItems.add(new MediaBrowserCompat.MediaItem(new MediaDescriptionCompat.Builder().setMediaId("iddopastor").setTitle("Pastor").build(), MediaBrowserCompat.MediaItem.FLAG_BROWSABLE));
} else if (parentId.equals("iddopastor")) {
mediaItems.add(new MediaBrowserCompat.MediaItem(new MediaDescriptionCompat.Builder().setMediaId("iddopastor2").setTitle("Pastor 2").build(), MediaBrowserCompat.MediaItem.FLAG_BROWSABLE));
// examine the passed parentMediaId to see which submenu we're at,
// and put the children of that menu in the mediaItems list
}
else if(parentId.equals("iddopastor2")){
mediaItems.add(new MediaBrowserCompat.MediaItem(new MediaDescriptionCompat.Builder().setMediaId("genealogia")
.setTitle("Tomé filho de José")
.setMediaUri(Uri.parse("http://70.38.6.72/~vivafe/web/wp-content/uploads/2016/08/Mateus-01.mp3"))
.setIconUri(Uri.parse("http://70.38.6.72/~vivafe/web/wp-content/uploads/2016/08/01.jpg"))
.build(), MediaBrowserCompat.MediaItem.FLAG_PLAYABLE));
}
result.sendResult(mediaItems);
}
#Override
public void onDestroy() {
mSession.release();
if( mediaPlayer != null ) {
pauseMedia();
mediaPlayer.release();
PreferenceManager.getDefaultSharedPreferences( this ).edit().putInt( CURRENT_MEDIA_POSITION,
0 ).commit();
}
super.onDestroy();
}
}
Sorry for the long code, but the erro might be anywhere on this class!
I've also put this on my AndroidManifest.xml:
<meta-data android:name="com.google.android.gms.car.application"
android:resource="#xml/automotive_app_desc"/>
<meta-data android:name="com.google.android.gms.car.notification.SmallIcon"
android:resource="#drawable/ic_notification" />
<service android:name=".Auto.AutoMediaBrowserService"
android:exported="true">
<intent-filter>
<action android:name=
"android.media.browse.MediaBrowserService"/>
</intent-filter>
</service>
And I have this on my res\xml\automotive_app_desc.xml:
<automotiveApp>
<uses name="media" />
</automotiveApp>
I think I might had the same problem. try to request audio focus before you start playing. I can hear my music on the desktop head unit
Related
So I'm attempting to have an audio stream play via the MediaPlayer Class. I was able to play the stream when only it in a Activity. Now I'm trying to have it work via a Service. I added comments to each functions so that I can track what its doing, everything seems to work fine, services get binded and all but now the onPrepared doesn't seems to initiate. I get no error until I try to stop the stream and says was stopped in State 4. I've tried reaching all possible cause and can't seem to find problem. I even build the unprepared within a new MediaPlayer class and still onPrepared doesn't get called. I've only put the service code below since all the other components work.
Any help would be great, Thank you.
Service:
public class StreamService extends Service
implements MediaPlayer.OnErrorListener,
MediaPlayer.OnCompletionListener,
MediaPlayer.OnPreparedListener,
AudioManager.OnAudioFocusChangeListener {
private static final String TAG = "TESTING";
private static final String TAGPlus = "Stream Service - ";
private static final String ACTION_PLAY = "PLAY";
private static final String ACTION_PREV = "PREV";
private static final String ACTION_NEXT = "NEXT";
public static MediaPlayer streamPlayer;
ActionPlaying actionPlaying;
AudioManager audioManager;
static ServiceState serviceState = StreamService.ServiceState.BLANK;
private boolean pausedTemporarilyDueToAudioFocus = false;
private boolean loweredVolumeDueToAudioFocus = false;
private IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
private BecomingNoisyReceiver myNoisyAudioStreamReceiver = new BecomingNoisyReceiver();
IBinder mBinder = new MyBinder();
#Nullable
#Override
public IBinder onBind(Intent intent) {
Log.d(TAG,TAGPlus+"onBind - "+intent);
return mBinder;
}
public class MyBinder extends Binder{
public StreamService getService(){
return StreamService.this;
}
}
#Override
public void onCreate() {
super.onCreate();
Log.d(TAG,TAGPlus+"onCreate..."+serviceState);
audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
initNoisyReceiver();
initMusicPlayer();
}
private void initNoisyReceiver() {
Log.d(TAG,TAGPlus+"initNoisyReceiver..."+serviceState);
//Handles headphones coming unplugged. cannot be done through a manifest receiver
IntentFilter filter = new IntentFilter();
filter.addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
filter.addAction(Intent.ACTION_HEADSET_PLUG);
registerReceiver(myNoisyAudioStreamReceiver, filter);
}
public void initMusicPlayer() {
Log.d(TAG,TAGPlus+"initMusicPlayer - "+serviceState+" - "+streamPlayer);
if (streamPlayer == null) {
serviceState = ServiceState.PREPARING;
streamPlayer = new MediaPlayer();
}
Log.d(TAG,TAGPlus+"initMusicPlayer - "+serviceState+" - "+streamPlayer);
streamPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
streamPlayer.setAudioAttributes(
new AudioAttributes
.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build());
// These are the events that will "wake us up"
//streamPlayer.setOnPreparedListener(this); // player initialized
streamPlayer.setOnCompletionListener(this); // song completed
streamPlayer.setOnErrorListener(this);
streamPlayer.setOnPreparedListener(this);
//setStreamDataSource();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
String action = intent.getStringExtra("myActionName");
Log.d(TAG,TAGPlus+"onStartCommand... - "+action+ "");
if (action != null) {
switch (action) {
case ACTION_PLAY:
if(actionPlaying != null)
{
actionPlaying.playClicked();
}
break;
case ACTION_PREV:
if(actionPlaying != null)
{
actionPlaying.prevClicked();
}
break;
case ACTION_NEXT:
if(actionPlaying != null)
{
actionPlaying.nextClicked();
}
break;
default:
}
}
return START_STICKY;
}
public void setStreamDataSource() {
Log.d(TAG,TAGPlus+"Setting DataSource... - "+streamPlayer+"");
if (streamPlayer == null) {
Log.d(TAG,TAGPlus+"MediaPlayer null. Call initMusicPlayer");
initMusicPlayer();
}
//streamPlayer.stop();
streamPlayer.reset();
//String url = "http://nap.casthost.net:8800/stream";
try {
streamPlayer.setDataSource("http://nap.casthost.net:8800/stream.mp3");
Log.d(TAG,TAGPlus+"Setting DataSource - Source Assigned...");
} catch (IOException e) {
Log.e(TAG, "IOException: couldn't start stream");
e.printStackTrace();
}
streamPlayer.prepareAsync();
}
public void playStream() {
Log.d(TAG,TAGPlus+"playStream... ");
String url = "http://nap.casthost.net:8800/stream.mp3";
streamPlayer.reset();
try {
streamPlayer.setDataSource(url);
} catch (IOException e) {e.printStackTrace();}
streamPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
Log.d(TAG,TAGPlus+"onPrepared..."+mediaPlayer);
serviceState = ServiceState.PREPARED;
streamPlayer.start();
serviceState = ServiceState.PLAYING;
}
});
streamPlayer.prepareAsync();
}
public void startStream() {
Log.d(TAG,TAGPlus+"startStream... "+serviceState+" - "+streamPlayer);
if (serviceState != ServiceState.PREPARED)
setStreamDataSource();
//startMusicPlayer();
playStream();
}
public void stopStream() {
Log.d(TAG,TAGPlus+"stopStream... "+serviceState+"");
serviceState = ServiceState.STOPPED;
stopMusicPlayer();
}
public void startMusicPlayer() {
Log.d(TAG,TAGPlus+"startMusicPlayer... "+serviceState+"");
if (serviceState == ServiceState.PLAYING)
return;
streamPlayer.start();
}
public void stopMusicPlayer() {
Log.d(TAG,TAGPlus+"stopMusicPlayer..."+streamPlayer);
if (streamPlayer == null)
return;
streamPlayer.stop();
serviceState = ServiceState.STOPPED;
}
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
Log.d(TAG,TAGPlus+"onPrepared..."+serviceState+" - "+mediaPlayer);
serviceState = ServiceState.PREPARED;
streamPlayer.start();
}
#Override
public void onDestroy() {
super.onDestroy();
streamPlayer.release();
streamPlayer = null;
//cancelNotification();
if (audioManager != null)
audioManager.abandonAudioFocus(this);
stopMusicPlayer();
unregisterReceiver(myNoisyAudioStreamReceiver);
}
public void setCallback(ActionPlaying actionPlaying) {
Log.d(TAG,TAGPlus+"setCallback...");
this.actionPlaying = actionPlaying;
}
private boolean requestAudioFocus() {
Log.d(TAG,TAGPlus+"requestAudioFocus... ");
//Request audio focus for playback
int result = audioManager.requestAudioFocus(
this,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
//Check if audio focus was granted. If not, stop the service.
return (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
}
#Override
public void onAudioFocusChange(int i) {
Log.d(TAG,TAGPlus+"onAudioFocusChange..."+i);
switch (i) {
// Yay, gained audio focus! Either from losing it for
// a long or short periods of time.
case AudioManager.AUDIOFOCUS_GAIN:
if (streamPlayer == null)
initMusicPlayer();
if (pausedTemporarilyDueToAudioFocus) {
pausedTemporarilyDueToAudioFocus = false;
//unpausePlayer();
}
if (loweredVolumeDueToAudioFocus) {
loweredVolumeDueToAudioFocus = false;
streamPlayer.setVolume(1.0f, 1.0f);
}
break;
// Damn, lost the audio focus for a (presumable) long time
case AudioManager.AUDIOFOCUS_LOSS:
//stopMusicPlayer();
break;
// Just lost audio focus but will get it back shortly
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
if (!isPaused()) {
//pausePlayer();
pausedTemporarilyDueToAudioFocus = true;
}
break;
// Temporarily lost audio focus but I can keep it playing
// at a low volume instead of stopping completely
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
Log.w(TAG, "audiofocus loss transient can duck");
streamPlayer.setVolume(0.1f, 0.1f);
loweredVolumeDueToAudioFocus = true;
break;
default:
throw new IllegalStateException("Unexpected value: " + i);
}
}
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
Log.d(TAG,TAGPlus+"onCompletion..."+mediaPlayer);
serviceState = ServiceState.PLAYING;
}
#Override
public boolean onError(MediaPlayer mediaPlayer, int i, int i1) {
Log.d(TAG,TAGPlus+"onError...");
mediaPlayer.reset();
streamPlayer.reset();
return false;
}
public boolean isPaused() {
Log.d(TAG,TAGPlus+"isPaused...");
return serviceState == ServiceState.STOPPED;
}
enum ServiceState {
BLANK,
PREPARING,
PREPARED,
PLAYING,
STOPPED
}
}
I created a service that extends MediaBrowserServiceCompat. This service holds a reference to my player and creates a new MediaSession with a callback. Everytime the player changes state, I update the MediaSession's playback state and create a MediaStyle notification. The notification is showing when I start to play something in my player, but the buttons in the notification are not triggering the MediaSession callback, they don't do anything. I'm setting the right flags in the MediaSession, I'm setting the session as active, I'm setting the correct actions in the playback state, I'm passing the session token to the notification but still not getting any callbacks from it. I really don't know what I'm doing wrong. All this code is inside a module imported by my app.
My NotificationHelper class:
private final MusicService mService;
private final NotificationCompat.Action mPlayAction;
private final NotificationCompat.Action mPauseAction;
private final NotificationCompat.Action mNextAction;
private final NotificationCompat.Action mPrevAction;
private final NotificationManager mNotificationManager;
public MediaNotificationManager(MusicService service) {
mService = service;
mNotificationManager =
(NotificationManager) mService.getSystemService(Context.NOTIFICATION_SERVICE);
mPlayAction =
new NotificationCompat.Action(
R.drawable.exo_icon_play,
"Play",
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService,
PlaybackStateCompat.ACTION_PLAY));
mPauseAction =
new NotificationCompat.Action(
R.drawable.exo_icon_pause,
"Pause",
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService,
PlaybackStateCompat.ACTION_PAUSE));
mNextAction =
new NotificationCompat.Action(
R.drawable.exo_icon_next,
"Next",
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService,
PlaybackStateCompat.ACTION_SKIP_TO_NEXT));
mPrevAction =
new NotificationCompat.Action(
R.drawable.exo_icon_previous,
"Previous",
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService,
PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS));
// Cancel all notifications to handle the case where the Service was killed and
// restarted by the system.
mNotificationManager.cancelAll();
}
public Notification getNotification(MediaMetadataCompat metadata,
#NonNull PlaybackStateCompat state,
MediaSessionCompat.Token token) {
boolean isPlaying = state.getState() == PlaybackStateCompat.STATE_PLAYING;
MediaDescriptionCompat description = metadata.getDescription();
NotificationCompat.Builder builder =
buildNotification(state, token, isPlaying, description);
return builder.build();
}
private NotificationCompat.Builder buildNotification(#NonNull PlaybackStateCompat state,
MediaSessionCompat.Token token,
boolean isPlaying,
MediaDescriptionCompat description) {
// Create the (mandatory) notification channel when running on Android Oreo.
if (isAndroidOOrHigher()) {
createChannel();
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(mService, CHANNEL_ID)
.setSmallIcon(R.drawable.exo_notification_small_icon)
.setContentTitle("Track title")
.setContentText("Artist - Album")
.setLargeIcon(BitmapFactory.decodeResource(mService.getResources(), R.drawable.exo_notification_small_icon))
.setStyle(new MediaStyle().setShowActionsInCompactView(0).setMediaSession(token));
builder.addAction(mPrevAction);
builder.addAction(isPlaying ? mPauseAction : mPlayAction);
builder.addAction(mNextAction);
return builder;
}
// Does nothing on versions of Android earlier than O.
#RequiresApi(Build.VERSION_CODES.O)
private void createChannel() {
if (mNotificationManager.getNotificationChannel(CHANNEL_ID) == null) {
// The user-visible name of the channel.
CharSequence name = "MediaSession";
// The user-visible description of the channel.
String description = "MediaSession and MediaPlayer";
int importance = NotificationManager.IMPORTANCE_LOW;
NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, name, importance);
// Configure the notification channel.
mChannel.setDescription(description);
mChannel.enableLights(true);
// Sets the notification light color for notifications posted to this
// channel, if the device supports this feature.
mChannel.setLightColor(Color.RED);
mChannel.enableVibration(true);
mChannel.setVibrationPattern(
new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
mNotificationManager.createNotificationChannel(mChannel);
Log.d(TAG, "createChannel: New channel created");
} else {
Log.d(TAG, "createChannel: Existing channel reused");
}
}
private boolean isAndroidOOrHigher() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
}
My Service class:
public class MusicService extends MediaBrowserServiceCompat {
private static final String TAG = MusicService.class.getSimpleName();
private MediaSessionCompat mSession;
private PlayerManager playerManager;
private MediaSessionCallback mCallback;
private MediaNotificationManager mediaNotificationManager;
#Override
public void onCreate() {
super.onCreate();
playerManager = PlayerManager.getInstance(this);
playerManager.addListener(new PlayerManagerServiceListener());
mediaNotificationManager = new MediaNotificationManager(this);
// Create a new MediaSession.
mSession = new MediaSessionCompat(this, "MusicService");
mCallback = new MediaSessionCallback();
mSession.setCallback(mCallback);
mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
setSessionToken(mSession.getSessionToken());
mSession.setActive(true);
}
#Override
public void onDestroy() {
mSession.release();
Log.d(TAG, "onDestroy: MediaPlayerAdapter stopped, and MediaSession released");
}
#Override
public BrowserRoot onGetRoot(#NonNull String clientPackageName,
int clientUid,
Bundle rootHints) {
return new BrowserRoot("root", null);
}
#Override
public void onLoadChildren(
#NonNull final String parentMediaId,
#NonNull final Result<List<MediaBrowserCompat.MediaItem>> result) {
result.sendResult(null);
}
// MediaSession Callback: Transport Controls -> MediaPlayerAdapter
public class MediaSessionCallback extends MediaSessionCompat.Callback {
#Override
public void onPlay() {
playerManager.play();
}
#Override
public void onPause() {
playerManager.pause();
}
#Override
public void onStop() {
playerManager.stop();
}
#Override
public void onSkipToNext() {
playerManager.next();
}
#Override
public void onSkipToPrevious() {
playerManager.previous();
}
#Override
public void onSeekTo(long pos) {
playerManager.seekTo(pos);
}
}
public class PlayerManagerServiceListener implements PlayerManager.PlayerManagerListener {
#Override
public void onError(#Nullable Exception error) {
}
#Override
public void onProgress(long duration, long position) {
}
#Override
public void onPlayerChange(int change) {
}
#Override
public void onTrackChange(TrackVO track) {
}
#Override
public void onListChange(List tracks) {
}
#Override
public void onPlaybackStateChange(int playbackState) {
PlaybackStateCompat.Builder playbackstateBuilder = new PlaybackStateCompat.Builder();
int playbackStateCompat = -1;
switch(playbackState) {
case PlaybackStateListener.STATE_PLAYING:
playbackStateCompat = PlaybackStateCompat.STATE_PLAYING;
//playbackstateBuilder.setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_PAUSE);
break;
case PlaybackStateListener.STATE_PAUSED:
playbackStateCompat = PlaybackStateCompat.STATE_PAUSED;
//playbackstateBuilder.setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_PLAY);
break;
}
if (playbackStateCompat == -1) {
return;
}
mSession.setActive(true);
playbackstateBuilder.setActions(
PlaybackStateCompat.ACTION_PLAY |
PlaybackStateCompat.ACTION_PLAY_PAUSE |
PlaybackStateCompat.ACTION_SKIP_TO_NEXT |
PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS);
playbackstateBuilder.setState(playbackStateCompat, PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 0);
PlaybackStateCompat state = playbackstateBuilder.build();
MediaMetadataCompat mediaMetadata = new MediaMetadataCompat.Builder()
.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, playerManager.getCurrenTrack().getName())
.build();
mSession.setMetadata(mediaMetadata);
mSession.setPlaybackState(state);
Notification notification = mediaNotificationManager.getNotification(
mediaMetadata,
state,
getSessionToken()
);
Intent intent = new Intent(MusicService.this, MusicService.class);
ContextCompat.startForegroundService(MusicService.this, intent);
startForeground(417, notification);
}
}
}
MediaBrowserHelper to initialize the service:
public class MediaBrowserHelper {
private static final String TAG = MediaBrowserHelper.class.getSimpleName();
private final Context mContext;
private final Class<? extends MediaBrowserServiceCompat> mMediaBrowserServiceClass;
private final List<Callback> mCallbackList = new ArrayList<>();
private final MediaBrowserConnectionCallback mMediaBrowserConnectionCallback;
private final MediaControllerCallback mMediaControllerCallback;
private final MediaBrowserSubscriptionCallback mMediaBrowserSubscriptionCallback;
private MediaBrowserCompat mMediaBrowser;
#Nullable
private MediaControllerCompat mMediaController;
public MediaBrowserHelper(Context context,
Class<? extends MediaBrowserServiceCompat> serviceClass) {
mContext = context;
mMediaBrowserServiceClass = serviceClass;
mMediaBrowserConnectionCallback = new MediaBrowserConnectionCallback();
mMediaControllerCallback = new MediaControllerCallback();
mMediaBrowserSubscriptionCallback = new MediaBrowserSubscriptionCallback();
}
public void onStart() {
if (mMediaBrowser == null) {
mMediaBrowser =
new MediaBrowserCompat(
mContext,
new ComponentName(mContext, mMediaBrowserServiceClass),
mMediaBrowserConnectionCallback,
null);
mMediaBrowser.connect();
}
Log.d(TAG, "onStart: Creating MediaBrowser, and connecting");
}
public void onStop() {
if (mMediaController != null) {
mMediaController.unregisterCallback(mMediaControllerCallback);
mMediaController = null;
}
if (mMediaBrowser != null && mMediaBrowser.isConnected()) {
mMediaBrowser.disconnect();
mMediaBrowser = null;
}
resetState();
Log.d(TAG, "onStop: Releasing MediaController, Disconnecting from MediaBrowser");
}
/**
* Called after connecting with a {#link MediaBrowserServiceCompat}.
* <p>
* Override to perform processing after a connection is established.
*
* #param mediaController {#link MediaControllerCompat} associated with the connected
* MediaSession.
*/
protected void onConnected(#NonNull MediaControllerCompat mediaController) {
}
/**
* Called after loading a browsable {#link MediaBrowserCompat.MediaItem}
*
* #param parentId The media ID of the parent item.
* #param children List (possibly empty) of child items.
*/
protected void onChildrenLoaded(#NonNull String parentId,
#NonNull List<MediaBrowserCompat.MediaItem> children) {
}
/**
* Called when the {#link MediaBrowserServiceCompat} connection is lost.
*/
protected void onDisconnected() {
}
#NonNull
protected final MediaControllerCompat getMediaController() {
if (mMediaController == null) {
throw new IllegalStateException("MediaController is null!");
}
return mMediaController;
}
/**
* The internal state of the app needs to revert to what it looks like when it started before
* any connections to the {#link MusicService} happens via the {#link MediaSessionCompat}.
*/
private void resetState() {
performOnAllCallbacks(new CallbackCommand() {
#Override
public void perform(#NonNull Callback callback) {
callback.onPlaybackStateChanged(null);
}
});
Log.d(TAG, "resetState: ");
}
public MediaControllerCompat.TransportControls getTransportControls() {
if (mMediaController == null) {
Log.d(TAG, "getTransportControls: MediaController is null!");
throw new IllegalStateException("MediaController is null!");
}
return mMediaController.getTransportControls();
}
public void registerCallback(Callback callback) {
if (callback != null) {
mCallbackList.add(callback);
// Update with the latest metadata/playback state.
if (mMediaController != null) {
final MediaMetadataCompat metadata = mMediaController.getMetadata();
if (metadata != null) {
callback.onMetadataChanged(metadata);
}
final PlaybackStateCompat playbackState = mMediaController.getPlaybackState();
if (playbackState != null) {
callback.onPlaybackStateChanged(playbackState);
}
}
}
}
private void performOnAllCallbacks(#NonNull CallbackCommand command) {
for (Callback callback : mCallbackList) {
if (callback != null) {
command.perform(callback);
}
}
}
/**
* Helper for more easily performing operations on all listening clients.
*/
private interface CallbackCommand {
void perform(#NonNull Callback callback);
}
// Receives callbacks from the MediaBrowser when it has successfully connected to the
// MediaBrowserService (MusicService).
private class MediaBrowserConnectionCallback extends MediaBrowserCompat.ConnectionCallback {
// Happens as a result of onStart().
#Override
public void onConnected() {
try {
// Get a MediaController for the MediaSession.
mMediaController =
new MediaControllerCompat(mContext, mMediaBrowser.getSessionToken());
mMediaController.registerCallback(mMediaControllerCallback);
// Sync existing MediaSession state to the UI.
mMediaControllerCallback.onMetadataChanged(mMediaController.getMetadata());
mMediaControllerCallback.onPlaybackStateChanged(
mMediaController.getPlaybackState());
MediaBrowserHelper.this.onConnected(mMediaController);
} catch (RemoteException e) {
Log.d(TAG, String.format("onConnected: Problem: %s", e.toString()));
throw new RuntimeException(e);
}
mMediaBrowser.subscribe(mMediaBrowser.getRoot(), mMediaBrowserSubscriptionCallback);
}
}
// Receives callbacks from the MediaBrowser when the MediaBrowserService has loaded new media
// that is ready for playback.
public class MediaBrowserSubscriptionCallback extends MediaBrowserCompat.SubscriptionCallback {
#Override
public void onChildrenLoaded(#NonNull String parentId,
#NonNull List<MediaBrowserCompat.MediaItem> children) {
MediaBrowserHelper.this.onChildrenLoaded(parentId, children);
}
}
// Receives callbacks from the MediaController and updates the UI state,
// i.e.: Which is the current item, whether it's playing or paused, etc.
private class MediaControllerCallback extends MediaControllerCompat.Callback {
#Override
public void onMetadataChanged(final MediaMetadataCompat metadata) {
performOnAllCallbacks(new CallbackCommand() {
#Override
public void perform(#NonNull Callback callback) {
callback.onMetadataChanged(metadata);
}
});
}
#Override
public void onPlaybackStateChanged(#Nullable final PlaybackStateCompat state) {
performOnAllCallbacks(new CallbackCommand() {
#Override
public void perform(#NonNull Callback callback) {
callback.onPlaybackStateChanged(state);
}
});
}
// This might happen if the MusicService is killed while the Activity is in the
// foreground and onStart() has been called (but not onStop()).
#Override
public void onSessionDestroyed() {
resetState();
onPlaybackStateChanged(null);
MediaBrowserHelper.this.onDisconnected();
}
}
}
Manifest:
<service android:name="com.amco.playermanager.MusicService">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService"/>
</intent-filter>
</service>
<receiver android:name="android.support.v4.media.session.MediaButtonReceiver">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON"/>
</intent-filter>
</receiver>
It turns out that the whole problem was caused by having another BroadcastReceiver handling MEDIA_BUTTON declared in my app's Manifest. By removing that receiver everything works now.
I know similar kind of question has been asked before, but none of them are helping me. I am building a music player in which I want to connect my Music through my music player like play, pause etc. Currently, media player service is connected with playlist activity but I have custom designed my media player and I am not able to connect it to the service class.
my MusicPlayer Activity is like this:
public class MusicPlayer extends AppCompatActivity implements PlaylistFragment.OnFragmentInteractionListener{
public static final String Broadcast_PLAY_NEW_AUDIO = "com.example.anuj.musicmetest.PlayNewAudio";
ImageView buttonPlayToggle;
MediaPlayer mediaPlayer;
MediaPlayerService player;
boolean serviceBound = false;
private ServiceConnection serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
MediaPlayerService.LocalBinder binder = (MediaPlayerService.LocalBinder) service;
player = binder.getService();
serviceBound = true;
}
#Override
public void onServiceDisconnected(ComponentName name) {
serviceBound = false;
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_player);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setTitle("Music Player");
buttonPlayToggle = (ImageView) findViewById(R.id.button_play_toggle);
buttonPlayToggle.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(mediaPlayer == null) return;
if (mediaPlayer.isPlaying()) {
updatePlayToggle(true);
mediaPlayer.pause();
} else {
updatePlayToggle(false);
mediaPlayer.start();
}
}
});
if (!serviceBound) {
Intent playerIntent = new Intent(this, MediaPlayerService.class);
startService(playerIntent);
bindService(playerIntent, serviceConnection, Context.BIND_AUTO_CREATE);
} else {
Intent broadcastIntent = new Intent(Broadcast_PLAY_NEW_AUDIO);
sendBroadcast(broadcastIntent);
}
}
public void updatePlayToggle(boolean play) {
buttonPlayToggle.setImageResource(play ? R.drawable.ic_pause : R.drawable.ic_play);
}
My ServicecClass is like this:
public class MediaPlayerService extends Service implements MediaPlayer.OnCompletionListener,
MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener, MediaPlayer.OnSeekCompleteListener,
MediaPlayer.OnInfoListener, MediaPlayer.OnBufferingUpdateListener,
AudioManager.OnAudioFocusChangeListener {
public static final String ACTION_PLAY = "com.example.anuj.musicmetest.ACTION_PLAY";
public static final String ACTION_PAUSE = "com.example.anuj.musicmetest.ACTION_PAUSE";
public static final String ACTION_PREVIOUS = "com.example.anuj.musicmetest.ACTION_PREVIOUS";
public static final String ACTION_NEXT = "com.example.anuj.musicmetest.ACTION_NEXT";
public static final String ACTION_STOP = "com.example.anuj.musicmetest.ACTION_STOP";
private MediaPlayer mediaPlayer;
private MediaSessionManager mediaSessionManager;
private MediaSessionCompat mediaSession;
private MediaControllerCompat.TransportControls transportControls;
private static final int NOTIFICATION_ID = 101;
//Used to pause/resume MediaPlayer
private int resumePosition;
//AudioFocus
private AudioManager audioManager;
// Binder given to clients
private final IBinder iBinder = new LocalBinder();
private ArrayList<Audio> audioList;
private int audioIndex = -1;
private Audio activeAudio; //an object on the currently playing audio
//Handle incoming phone calls
private boolean ongoingCall = false;
private PhoneStateListener phoneStateListener;
private TelephonyManager telephonyManager;
/**
* Service lifecycle methods
*/
#Override
public IBinder onBind(Intent intent) {
return iBinder;
}
#Override
public void onCreate() {
super.onCreate();
callStateListener();
//ACTION_AUDIO_BECOMING_NOISY -- change in audio outputs -- BroadcastReceiver
registerBecomingNoisyReceiver();
//Listen for new Audio to play -- BroadcastReceiver
register_playNewAudio();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
try {
//Load data from SharedPreferences
StorageUtil storage = new StorageUtil(getApplicationContext());
audioList = storage.loadAudio();
audioIndex = storage.loadAudioIndex();
if (audioIndex != -1 && audioIndex < audioList.size()) {
//index is in a valid range
activeAudio = audioList.get(audioIndex);
} else {
stopSelf();
}
} catch (NullPointerException e) {
stopSelf();
}
//Request audio focus
if (!requestAudioFocus()) {
//Could not gain focus
stopSelf();
}
if (mediaSessionManager == null) {
try {
initMediaSession();
initMediaPlayer();
} catch (RemoteException e) {
e.printStackTrace();
stopSelf();
}
buildNotification(PlaybackStatus.PLAYING);
}
//Handle Intent action from MediaSession.TransportControls
handleIncomingActions(intent);
return super.onStartCommand(intent, flags, startId);
}
#Override
public boolean onUnbind(Intent intent) {
mediaSession.release();
removeNotification();
return super.onUnbind(intent);
}
#Override
public void onDestroy() {
super.onDestroy();
if (mediaPlayer != null) {
stopMedia();
mediaPlayer.release();
}
removeAudioFocus();
//Disable the PhoneStateListener
if (phoneStateListener != null) {
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
}
removeNotification();
//unregister BroadcastReceivers
unregisterReceiver(becomingNoisyReceiver);
unregisterReceiver(playNewAudio);
//clear cached playlist
new StorageUtil(getApplicationContext()).clearCachedAudioPlaylist();
}
/**
* Service Binder
*/
public class LocalBinder extends Binder {
public MediaPlayerService getService() {
// Return this instance of LocalService so clients can call public methods
return MediaPlayerService.this;
}
}
/**
* MediaPlayer callback methods
*/
#Override
public void onBufferingUpdate(MediaPlayer mp, int percent) {
//Invoked indicating buffering status of
//a media resource being streamed over the network.
}
#Override
public void onCompletion(MediaPlayer mp) {
//Invoked when playback of a media source has completed.
stopMedia();
removeNotification();
//stop the service
stopSelf();
}
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
//Invoked when there has been an error during an asynchronous operation
switch (what) {
case MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK:
Log.d("MediaPlayer Error", "MEDIA ERROR NOT VALID FOR PROGRESSIVE PLAYBACK " + extra);
break;
case MediaPlayer.MEDIA_ERROR_SERVER_DIED:
Log.d("MediaPlayer Error", "MEDIA ERROR SERVER DIED " + extra);
break;
case MediaPlayer.MEDIA_ERROR_UNKNOWN:
Log.d("MediaPlayer Error", "MEDIA ERROR UNKNOWN " + extra);
break;
}
return false;
}
#Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
//Invoked to communicate some info
return false;
}
#Override
public void onPrepared(MediaPlayer mp) {
//Invoked when the media source is ready for playback.
playMedia();
}
#Override
public void onSeekComplete(MediaPlayer mp) {
//Invoked indicating the completion of a seek operation.
}
#Override
public void onAudioFocusChange(int focusState) {
//Invoked when the audio focus of the system is updated.
switch (focusState) {
case AudioManager.AUDIOFOCUS_GAIN:
// resume playback
if (mediaPlayer == null) initMediaPlayer();
else if (!mediaPlayer.isPlaying()) mediaPlayer.start();
mediaPlayer.setVolume(1.0f, 1.0f);
break;
case AudioManager.AUDIOFOCUS_LOSS:
// Lost focus for an unbounded amount of time: stop playback and release media player
if (mediaPlayer.isPlaying()) mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
if (mediaPlayer.isPlaying()) mediaPlayer.pause();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
// Lost focus for a short time, but it's ok to keep playing
// at an attenuated level
if (mediaPlayer.isPlaying()) mediaPlayer.setVolume(0.1f, 0.1f);
break;
}
}
/**
* AudioFocus
*/
private boolean requestAudioFocus() {
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
//Focus gained
return true;
}
//Could not gain focus
return false;
}
private boolean removeAudioFocus() {
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED ==
audioManager.abandonAudioFocus(this);
}
/**
* MediaPlayer actions
*/
private void initMediaPlayer() {
if (mediaPlayer == null)
mediaPlayer = new MediaPlayer();//new MediaPlayer instance
//Set up MediaPlayer event listeners
mediaPlayer.setOnCompletionListener(this);
mediaPlayer.setOnErrorListener(this);
mediaPlayer.setOnPreparedListener(this);
mediaPlayer.setOnBufferingUpdateListener(this);
mediaPlayer.setOnSeekCompleteListener(this);
mediaPlayer.setOnInfoListener(this);
//Reset so that the MediaPlayer is not pointing to another data source
mediaPlayer.reset();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
// Set the data source to the mediaFile location
mediaPlayer.setDataSource(activeAudio.getLocalData());
} catch (IOException e) {
e.printStackTrace();
stopSelf();
}
mediaPlayer.prepareAsync();
}
private void playMedia() {
if (!mediaPlayer.isPlaying()) {
mediaPlayer.start();
}
}
private void stopMedia() {
if (mediaPlayer == null) return;
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
}
private void pauseMedia() {
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();
resumePosition = mediaPlayer.getCurrentPosition();
}
}
private void resumeMedia() {
if (!mediaPlayer.isPlaying()) {
mediaPlayer.seekTo(resumePosition);
mediaPlayer.start();
}
}
private void skipToNext() {
if (audioIndex == audioList.size() - 1) {
//if last in playlist
audioIndex = 0;
activeAudio = audioList.get(audioIndex);
} else {
//get next in playlist
activeAudio = audioList.get(++audioIndex);
}
//Update stored index
new StorageUtil(getApplicationContext()).storeAudioIndex(audioIndex);
stopMedia();
//reset mediaPlayer
mediaPlayer.reset();
initMediaPlayer();
}
private void skipToPrevious() {
if (audioIndex == 0) {
//if first in playlist
//set index to the last of audioList
audioIndex = audioList.size() - 1;
activeAudio = audioList.get(audioIndex);
} else {
//get previous in playlist
activeAudio = audioList.get(--audioIndex);
}
//Update stored index
new StorageUtil(getApplicationContext()).storeAudioIndex(audioIndex);
stopMedia();
//reset mediaPlayer
mediaPlayer.reset();
initMediaPlayer();
}
/**
* ACTION_AUDIO_BECOMING_NOISY -- change in audio outputs
*/
private BroadcastReceiver becomingNoisyReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//pause audio on ACTION_AUDIO_BECOMING_NOISY
pauseMedia();
buildNotification(PlaybackStatus.PAUSED);
}
};
private void registerBecomingNoisyReceiver() {
//register after getting audio focus
IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
registerReceiver(becomingNoisyReceiver, intentFilter);
}
/**
* Handle PhoneState changes
*/
private void callStateListener() {
// Get the telephony manager
telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
//Starting listening for PhoneState changes
phoneStateListener = new PhoneStateListener() {
#Override
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
//if at least one call exists or the phone is ringing
//pause the MediaPlayer
case TelephonyManager.CALL_STATE_OFFHOOK:
case TelephonyManager.CALL_STATE_RINGING:
if (mediaPlayer != null) {
pauseMedia();
ongoingCall = true;
}
break;
case TelephonyManager.CALL_STATE_IDLE:
// Phone idle. Start playing.
if (mediaPlayer != null) {
if (ongoingCall) {
ongoingCall = false;
resumeMedia();
}
}
break;
}
}
};
// Register the listener with the telephony manager
// Listen for changes to the device call state.
telephonyManager.listen(phoneStateListener,
PhoneStateListener.LISTEN_CALL_STATE);
}
/**
* MediaSession and Notification actions
*/
private void initMediaSession() throws RemoteException {
if (mediaSessionManager != null) return; //mediaSessionManager exists
mediaSessionManager = (MediaSessionManager) getSystemService(Context.MEDIA_SESSION_SERVICE);
// Create a new MediaSession
mediaSession = new MediaSessionCompat(getApplicationContext(), "AudioPlayer");
//Get MediaSessions transport controls
transportControls = mediaSession.getController().getTransportControls();
//set MediaSession -> ready to receive media commands
mediaSession.setActive(true);
//indicate that the MediaSession handles transport control commands
// through its MediaSessionCompat.Callback.
mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
//Set mediaSession's MetaData
updateMetaData();
// Attach Callback to receive MediaSession updates
mediaSession.setCallback(new MediaSessionCompat.Callback() {
// Implement callbacks
#Override
public void onPlay() {
super.onPlay();
resumeMedia();
buildNotification(PlaybackStatus.PLAYING);
}
#Override
public void onPause() {
super.onPause();
pauseMedia();
buildNotification(PlaybackStatus.PAUSED);
}
#Override
public void onSkipToNext() {
super.onSkipToNext();
skipToNext();
updateMetaData();
buildNotification(PlaybackStatus.PLAYING);
}
#Override
public void onSkipToPrevious() {
super.onSkipToPrevious();
skipToPrevious();
updateMetaData();
buildNotification(PlaybackStatus.PLAYING);
}
#Override
public void onStop() {
super.onStop();
removeNotification();
//Stop the service
stopSelf();
}
#Override
public void onSeekTo(long position) {
super.onSeekTo(position);
}
});
}
private void updateMetaData() {
mediaSession.setMetadata(new MediaMetadataCompat.Builder()
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, activeAudio.getLocalArtist())
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, activeAudio.getLocalAlbum())
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, activeAudio.getLocalTitle())
.build());
}
private void buildNotification(PlaybackStatus playbackStatus) {
int notificationAction = android.R.drawable.ic_media_pause;//needs to be initialized
PendingIntent play_pauseAction = null;
//Build a new notification according to the current state of the MediaPlayer
if (playbackStatus == PlaybackStatus.PLAYING) {
notificationAction = android.R.drawable.ic_media_pause;
//create the pause action
play_pauseAction = playbackAction(1);
} else if (playbackStatus == PlaybackStatus.PAUSED) {
notificationAction = android.R.drawable.ic_media_play;
//create the play action
play_pauseAction = playbackAction(0);
}
// Create a new Notification
NotificationCompat.Builder notificationBuilder = (NotificationCompat.Builder) new NotificationCompat.Builder(this)
// Hide the timestamp
.setShowWhen(false)
// Set the Notification style
.setStyle(new NotificationCompat.MediaStyle()
// Attach our MediaSession token
.setMediaSession(mediaSession.getSessionToken())
// Show our playback controls in the compat view
.setShowActionsInCompactView(0, 1, 2))
// Set the Notification color
.setColor(getResources().getColor(R.color.colorAccent))
// Set the large and small icons
.setSmallIcon(android.R.drawable.stat_sys_headset)
// Set Notification content information
.setContentText(activeAudio.getLocalArtist())
.setContentTitle(activeAudio.getLocalAlbum())
.setContentInfo(activeAudio.getLocalTitle())
// Add playback actions
.addAction(android.R.drawable.ic_media_previous, "previous", playbackAction(3))
.addAction(notificationAction, "pause", play_pauseAction)
.addAction(android.R.drawable.ic_media_next, "next", playbackAction(2));
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).notify(NOTIFICATION_ID, notificationBuilder.build());
}
private PendingIntent playbackAction(int actionNumber) {
Intent playbackAction = new Intent(this, MediaPlayerService.class);
switch (actionNumber) {
case 0:
// Play
playbackAction.setAction(ACTION_PLAY);
return PendingIntent.getService(this, actionNumber, playbackAction, 0);
case 1:
// Pause
playbackAction.setAction(ACTION_PAUSE);
return PendingIntent.getService(this, actionNumber, playbackAction, 0);
case 2:
// Next track
playbackAction.setAction(ACTION_NEXT);
return PendingIntent.getService(this, actionNumber, playbackAction, 0);
case 3:
// Previous track
playbackAction.setAction(ACTION_PREVIOUS);
return PendingIntent.getService(this, actionNumber, playbackAction, 0);
default:
break;
}
return null;
}
private void removeNotification() {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(NOTIFICATION_ID);
}
private void handleIncomingActions(Intent playbackAction) {
if (playbackAction == null || playbackAction.getAction() == null) return;
String actionString = playbackAction.getAction();
if (actionString.equalsIgnoreCase(ACTION_PLAY)) {
transportControls.play();
} else if (actionString.equalsIgnoreCase(ACTION_PAUSE)) {
transportControls.pause();
} else if (actionString.equalsIgnoreCase(ACTION_NEXT)) {
transportControls.skipToNext();
} else if (actionString.equalsIgnoreCase(ACTION_PREVIOUS)) {
transportControls.skipToPrevious();
} else if (actionString.equalsIgnoreCase(ACTION_STOP)) {
transportControls.stop();
}
}
/**
* Play new Audio
*/
private BroadcastReceiver playNewAudio = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//Get the new media index form SharedPreferences
audioIndex = new StorageUtil(getApplicationContext()).loadAudioIndex();
if (audioIndex != -1 && audioIndex < audioList.size()) {
//index is in a valid range
activeAudio = audioList.get(audioIndex);
} else {
stopSelf();
}
//A PLAY_NEW_AUDIO action received
//reset mediaPlayer to play the new Audio
stopMedia();
mediaPlayer.reset();
initMediaPlayer();
updateMetaData();
buildNotification(PlaybackStatus.PLAYING);
}
};
private void register_playNewAudio() {
//Register playNewMedia receiver
IntentFilter filter = new IntentFilter(FragmentRight.Broadcast_PLAY_NEW_AUDIO);
registerReceiver(playNewAudio, filter);
}
And my Playlist fragment is like this:
public class FragmentRight extends Fragment {
public static final String Broadcast_PLAY_NEW_AUDIO = "com.example.anuj.musicmetest.PlayNewAudio";
private OnFragmentInteractionListener mListener;
ArrayList<Audio> localList;
TextView nameView;
private MediaPlayerService mediaPlayer;
boolean serviceBound = false;
public FragmentRight() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_right, container, false);
nameView = (TextView) rootView.findViewById(R.id.name1);
loadAudio();
RecyclerView recyclerView1 = (RecyclerView) rootView.findViewById(R.id.recycler_view1);
LocalListAdapter localListAdapteradapter = new LocalListAdapter(getActivity().getApplication(), localList);
recyclerView1.setAdapter(localListAdapteradapter);
recyclerView1.setLayoutManager(new LinearLayoutManager(getActivity().getApplication()));
recyclerView1.addOnItemTouchListener(new CustomTouchListener(getActivity(), new onItemClickListener() {
#Override
public void onClick(View view, int index) {
playAudio(index);
Intent intent = new Intent(getActivity(), MusicPlayer.class);
startActivity(intent);
}
}));
return rootView;
}
private ServiceConnection serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
MediaPlayerService.LocalBinder binder = (MediaPlayerService.LocalBinder) service;
mediaPlayer = binder.getService();
serviceBound = true;
}
#Override
public void onServiceDisconnected(ComponentName name) {
serviceBound = false;
}
};
private void playAudio(int audioIndex) {
//Check is service is active
if (!serviceBound) {
//Store Serializable audioList to SharedPreferences
StorageUtil storage = new StorageUtil(getActivity().getApplicationContext());
storage.storeAudio(localList);
storage.storeAudioIndex(audioIndex);
Intent playerIntent = new Intent(getActivity(), MediaPlayerService.class);
getActivity().startService(playerIntent);
getActivity().bindService(playerIntent, serviceConnection, Context.BIND_AUTO_CREATE);
} else {
//Store the new audioIndex to SharedPreferences
StorageUtil storage = new StorageUtil(getActivity().getApplicationContext());
storage.storeAudioIndex(audioIndex);
Intent broadcastIntent = new Intent(Broadcast_PLAY_NEW_AUDIO);
getActivity().sendBroadcast(broadcastIntent);
}
}
private void loadAudio() {
ContentResolver contentResolver = getActivity().getContentResolver();
Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
String selection = MediaStore.Audio.Media.IS_MUSIC + "!= 0";
String sortOrder = MediaStore.Audio.Media.TITLE + " ASC";
Cursor cursor = contentResolver.query(uri, null, selection, null, sortOrder);
if (cursor != null && cursor.getCount() > 0) {
localList = new ArrayList<>();
while (cursor.moveToNext()) {
String localData = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA));
String localTitle = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.TITLE));
String localAlbum = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM));
String localArtist = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST));
// Save to audioList
localList.add(new Audio(localData, localTitle, localAlbum, localArtist));
}
}
cursor.close();
}
// TODO: Rename method, update argument and hook method into UI event
public void onButtonPressed(Uri uri) {
if (mListener != null) {
mListener.onFragmentInteraction(uri);
}
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
void onFragmentInteraction(Uri uri);
}
#Override
public void onDestroy() {
super.onDestroy();
if (serviceBound) {
getContext().unbindService(serviceConnection);
//service is active
mediaPlayer.stopSelf();
}
}
I am developing an audio recorder with Sony Smart Eyeglass, but it does not work well.
My application records just a voice from phone microphone, not from Smart Eyeglass microphone.
I'd like to record a voice just only from Smart Eyeglass microphone.
Any ideas?
Here is my code.
public class AudioRecordControl extends ControlExtension {
private final AudioManager _audioManager;
private final File _file;
private SmartEyeglassControlUtils _util;
private static final int SMARTEYEGLASS_API_VERSION = 1;
private MediaRecorder _recorder;
private MediaPlayer _player;
enum State {
STOP,
RECORDING,
PLAYING,
}
private State _state;
public AudioRecordControl(Context context, String hostAppPackageName) {
super(context, hostAppPackageName);
_util = new SmartEyeglassControlUtils(hostAppPackageName, new SmartEyeglassEventListener() {
#Override
public void onDialogClosed(int code) {
super.onDialogClosed(code);
doNextAction(code);
showCurrentLayout();
}
});
_util.setRequiredApiVersion(SMARTEYEGLASS_API_VERSION);
_util.activate(context);
_audioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
_audioManager.setMode(AudioManager.MODE_IN_CALL);
_audioManager.startBluetoothSco();
_state = State.STOP;
File directoryPath = Environment.getExternalStoragePublicDirectory("AudioRecord");
if (!directoryPath.exists()) {
if (!directoryPath.mkdirs()) {
Log.e(Constants.LOG_TAG, "failed to create directory '" + directoryPath.toString() + "'");
}
}
_file = new File(directoryPath, "record.3gp");
showCurrentLayout();
}
private void showCurrentLayout() {
showLayout(R.layout.layout, null);
switch (_state) {
case STOP:
String[] buttons;
if (_file.exists()) {
buttons = new String[]{
mContext.getString(R.string.record),
mContext.getString(R.string.play)};
} else {
buttons = new String[]{mContext.getString(R.string.record)};
}
_util.showDialogMessage(
mContext.getString(R.string.title),
mContext.getString(R.string.choose_one), buttons);
break;
case RECORDING:
_util.showDialogMessage(
mContext.getString(R.string.stop_recording),
com.sony.smarteyeglass.SmartEyeglassControl.Intents.DIALOG_MODE_OK);
break;
case PLAYING:
_util.showDialogMessage(
mContext.getString(R.string.stop_playing),
com.sony.smarteyeglass.SmartEyeglassControl.Intents.DIALOG_MODE_OK);
break;
default:
break;
}
}
private void doNextAction(int code) {
if (code == -1) {
stopRequest();
}
showLayout(R.layout.layout, null);
switch (_state) {
case STOP:
if (code == 0) {
Log.d(Constants.LOG_TAG, "start recording");
try {
_startRecording();
} catch (IOException e) {
Log.e(Constants.LOG_TAG, "failed to record", e);
_util.showDialogMessage(
mContext.getString(R.string.failed_to_record),
SmartEyeglassControl.Intents.DIALOG_MODE_TIMEOUT);
_recorder = null;
return;
}
_state = State.RECORDING;
} else if (code == 1) {
Log.d(Constants.LOG_TAG, "start playing");
try {
_play();
} catch (IOException e) {
Log.e(Constants.LOG_TAG, "failed to play", e);
_util.showDialogMessage(
mContext.getString(R.string.failed_to_pla),
SmartEyeglassControl.Intents.DIALOG_MODE_TIMEOUT);
_player = null;
return;
}
_state = State.PLAYING;
} else {
stopRequest();
}
break;
case RECORDING:
Log.d(Constants.LOG_TAG, "stop recording");
_stopRecord();
_state = State.STOP;
break;
case PLAYING:
Log.d(Constants.LOG_TAG, "stop playing");
_stopPlay();
_state = State.STOP;
break;
default:
_state = State.STOP;
break;
}
}
private void _startRecording() throws IOException {
if (_recorder == null) {
_recorder = new MediaRecorder();
_recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
_recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
_recorder.setOutputFile(String.valueOf(_file));
_recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
_recorder.prepare();
_recorder.start();
}
}
private void _stopRecord() {
if (_recorder != null) {
_recorder.stop();
_recorder.reset();
_recorder.release();
_recorder = null;
}
}
private void _play() throws IOException {
if (_player == null) {
_player = new MediaPlayer();
_player.setDataSource(String.valueOf(_file));
_player.prepare();
_player.start();
}
}
private void _stopPlay() {
if (_player != null) {
_player.stop();
_player.reset();
_player.release();
_player= null;
}
}
#Override
public void onResume() {
showCurrentLayout();
}
#Override
public void onTap(int action, long timeStamp) {
super.onTap(action, timeStamp);
showCurrentLayout();
}
#Override
public void onDestroy() {
Log.d(Constants.LOG_TAG, "Control On Desuptroy");
_util.deactivate();
_audioManager.setMode(AudioManager.MODE_NORMAL);
_audioManager.stopBluetoothSco();
}
}
Some phones require an extra method call after setMode:
private AudioManager m_amAudioManager;
m_amAudioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
m_amAudioManager.setMode(AudioManager.MODE_IN_CALL);
m_amAudioManager.setSpeakerphoneOn(false);
To learn more about rest of the operation, there is a guide on Audio I/O with SmartEyeglass:
https://developer.sony.com/develop/wearables/smarteyeglass-sdk/guides/use-bluetooth-for-audio-io/
Would you put your recording code in the Activity and test without using the eyeglass first?
As you are using the phone's capability only, it should work if you can record from microphone in Activity alone. Please ensure you have added uses-permission tag in AndroidManifest.xml
I spend about 20 hours until now and my problem there still is .
I am creating an android application that has several Activities (mainMenu , aboutUs,setting).
I followed the best answered of below link and that was O.K .
Music played using asyncTask isn't stopping by using cancel
When i run my app,(my code is in mainActivity ), the music begin and it does not stop when navigate to other Activities . This is GOOD .
But i put a ToggleButton in my setting_activity Activity that i hope this Button starts and stops this music. NOW my question is how can i stop and/or start music again from setting_activity ?
in another solution :
I create a class MusicManager and i call it`s start and stop .
But this was several problems too :
Music started in mainMenu_activity but only play for about 15 seconds and then stopped.
I could not stop the music from another activities.At this time i play music in mainMenua_ctivity as this line codes :
MusicManager mm = new MusicManager(this, R.raw.background_sound);
mm.play();
How can i stop playing that ?
3. The music stopped when i navigate to other activities .
public class MusicManager implements OnPreparedListener {
static MediaPlayer mPlayer;
Context context;
private int mySoundId;
public MusicManager(Context ctx, int musicID) {
context = ctx;
mySoundId = musicID;
mPlayer = MediaPlayer.create(context, mySoundId);
mPlayer.setOnPreparedListener(this);
}
public void play() {
mPlayer = MediaPlayer.create(context, mySoundId);
}
public void stop() {
mPlayer.stop();
mPlayer.release();
}
#Override
public void onPrepared(MediaPlayer player) {
player.start();
mPlayer.setLooping(true);
mPlayer.setVolume(25, 25);
}
}
Finally i want to play a background music in all activities without stop/start music .
How can i do it ?
You could put the music player in a service. This would make it independent from the Activities and you would still be able to control the playback through intents.
Here are some code example about it: https://stackoverflow.com/a/8209975/2804473
The code below is written by Synxmax here at StackOverflow, and covered in the link above:
public class BackgroundSoundService extends Service {
private static final String TAG = null;
MediaPlayer player;
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
player = MediaPlayer.create(this, R.raw.idil);
player.setLooping(true); // Set looping
player.setVolume(100,100);
}
public int onStartCommand(Intent intent, int flags, int startId) {
player.start();
return 1;
}
public void onStart(Intent intent, int startId) {
// TO DO
}
public IBinder onUnBind(Intent arg0) {
// TO DO Auto-generated method
return null;
}
public void onStop() {
}
public void onPause() {
}
#Override
public void onDestroy() {
player.stop();
player.release();
}
#Override
public void onLowMemory() {
}
}
#Override
public void onCreate (){
super.onCreate();
Player = MediaPlayer.create(this, R.raw.jingle);
mPlayer.setOnErrorListener(this);
if(mPlayer!= null)
{
mPlayer.setLooping(true);
mPlayer.setVolume(100,100);
}
mPlayer.setOnErrorListener(new OnErrorListener() {
public boolean onError(MediaPlayer mp, int what, int
extra){
onError(mPlayer, what, extra);
return true;
}
});
}
public class serv extends Service{
MediaPlayer mp;
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
public void onCreate()
{
mp = MediaPlayer.create(this, R.raw.b);
mp.setLooping(false);
}
public void onDestroy()
{
mp.stop();
}
public void onStart(Intent intent,int startid){
Log.d(tag, "On start");
mp.start();
}
}
The top answer is correct, however you have to add the service to the manifest file.
<service android:enabled="true" android:name="BackgroundSoundService" />
Simon's answer above is correct. I had similar problem where I have fragments which had music player and I needed to go back to that UI on click of a button. Your case is similar but instead of going back to UI, you want to control playback. Here is what I did for my application. This takes care of playback of audio list including shuffle and repeat functionality. This takes care of showing media controls in notification bar too.
Create a service MusicPlayerService with following code:
public class MediaPlayerService extends Service implements MediaPlayer.OnCompletionListener,
MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener, MediaPlayer.OnSeekCompleteListener,
MediaPlayer.OnInfoListener, MediaPlayer.OnBufferingUpdateListener,
AudioManager.OnAudioFocusChangeListener {
public static final String ACTION_PLAY = "pkg_name.ACTION_PLAY";
public static final String ACTION_PAUSE = "pkg_name.ACTION_PAUSE";
public static final String ACTION_PREVIOUS = "pkg_name.ACTION_PREVIOUS";
public static final String ACTION_NEXT = "pkg_name.ACTION_NEXT";
public static final String ACTION_STOP = "pkg_name.ACTION_STOP";
private MediaPlayer mediaPlayer;
//MediaSession
private MediaSessionManager mediaSessionManager;
private MediaSessionCompat mediaSession;
private MediaControllerCompat.TransportControls transportControls;
//AudioPlayer notification ID
private static final int NOTIFICATION_ID = 101;
//Used to pause/resume MediaPlayer
private int resumePosition;
// Binder given to clients
private final IBinder iBinder = new LocalBinder();
//List of available Audio files
private ArrayList<PlayableTrack> audioList;
private int audioIndex = -1;
//Handle incoming phone calls
private boolean ongoingCall = false;
private PhoneStateListener phoneStateListener;
private TelephonyManager telephonyManager;
private Bitmap albumArtBitmap;
private boolean shuffle = false;
private boolean repeat = false;
private Random rand;
/**
* Service lifecycle methods
*/
#Override
public IBinder onBind(Intent intent) {
return iBinder;
}
#Override
public void onCreate() {
super.onCreate();
// Perform one-time setup procedures
// Manage incoming phone calls during playback.
// Pause MediaPlayer on incoming call,
// Resume on hangup.
callStateListener();
//ACTION_AUDIO_BECOMING_NOISY -- change in audio outputs -- BroadcastReceiver
registerBecomingNoisyReceiver();
//Listen for new Audio to play -- BroadcastReceiver
register_playNewAudio();
rand = new Random();
StorageUtil storage = new StorageUtil(getApplicationContext());
shuffle = storage.loadShuffleRepeat("Shuffle");
repeat = storage.loadShuffleRepeat("Repeat");
}
//The system calls this method when an activity, requests the service be started
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
try {
//Load data from SharedPreferences
StorageUtil storage = new StorageUtil(getApplicationContext());
audioList = storage.loadAudio();
audioIndex = storage.loadAudioIndex();
if (audioIndex != -1 && audioIndex < audioList.size()) {
//index is in a valid range
activeAudio = audioList.get(audioIndex);
} else {
stopSelf();
}
} catch (NullPointerException e) {
stopSelf();
}
if (mediaSessionManager == null) {
try {
initMediaSession();
initMediaPlayer();
} catch (RemoteException e) {
e.printStackTrace();
stopSelf();
}
buildNotification(PlaybackStatus.PLAYING);
}
//Handle Intent action from MediaSession.TransportControls
handleIncomingActions(intent);
return super.onStartCommand(intent, flags, startId);
}
#Override
public boolean onUnbind(Intent intent) {
mediaSession.release();
removeNotification();
return super.onUnbind(intent);
}
#Override
public void onDestroy() {
super.onDestroy();
if (mediaPlayer != null) {
stopMedia();
mediaPlayer.reset();
}
//Disable the PhoneStateListener
if (phoneStateListener != null) {
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
}
removeNotification();
//unregister BroadcastReceivers
unregisterReceiver(becomingNoisyReceiver);
unregisterReceiver(playNewAudio);
Picasso.get().cancelRequest(target);
}
/**
* Service Binder
*/
public class LocalBinder extends Binder {
public MediaPlayerService getService() {
// Return this instance of LocalService so clients can call public methods
return MediaPlayerService.this;
}
}
/**
* MediaPlayer callback methods
*/
#Override
public void onBufferingUpdate(MediaPlayer mp, int percent) {
//Invoked indicating buffering status of
//a media resource being streamed over the network.
}
#Override
public void onCompletion(MediaPlayer mp) {
//Invoked when playback of a media source has completed.
stopMedia();
}
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
//Invoked when there has been an error during an asynchronous operation
switch (what) {
case MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK:
Log.d("MediaPlayer Error", "MEDIA ERROR NOT VALID FOR PROGRESSIVE PLAYBACK " + extra);
break;
case MediaPlayer.MEDIA_ERROR_SERVER_DIED:
Log.d("MediaPlayer Error", "MEDIA ERROR SERVER DIED " + extra);
break;
case MediaPlayer.MEDIA_ERROR_UNKNOWN:
Log.d("MediaPlayer Error", "MEDIA ERROR UNKNOWN " + extra);
break;
}
return false;
}
#Override
public void onPrepared(MediaPlayer mp) {
//Invoked when the media source is ready for playback.
playMedia();
}
#Override
public void onAudioFocusChange(int focusState) {
//Invoked when the audio focus of the system is updated.
switch (focusState) {
case AudioManager.AUDIOFOCUS_GAIN:
// resume playback
if (mediaPlayer == null) initMediaPlayer();
else if (!mediaPlayer.isPlaying()) mediaPlayer.start();
mediaPlayer.setVolume(1.0f, 1.0f);
break;
case AudioManager.AUDIOFOCUS_LOSS:
// Lost focus for an unbounded amount of time: stop playback and release media player
if (mediaPlayer.isPlaying()) mediaPlayer.stop();
mediaPlayer.reset();
mediaPlayer = null;
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
// Lost focus for a short time, but we have to stop
// playback. We don't release the media player because playback
// is likely to resume
if (mediaPlayer.isPlaying()) mediaPlayer.pause();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
// Lost focus for a short time, but it's ok to keep playing
// at an attenuated level
if (mediaPlayer.isPlaying()) mediaPlayer.setVolume(0.1f, 0.1f);
break;
}
}
/**
* MediaPlayer actions
*/
private void initMediaPlayer() {
if (mediaPlayer == null)
mediaPlayer = new MediaPlayer();//new MediaPlayer instance
//Set up MediaPlayer event listeners
mediaPlayer.setOnCompletionListener(this);
mediaPlayer.setOnErrorListener(this);
mediaPlayer.setOnPreparedListener(this);
mediaPlayer.setOnBufferingUpdateListener(this);
mediaPlayer.setOnSeekCompleteListener(this);
mediaPlayer.setOnInfoListener(this);
//Reset so that the MediaPlayer is not pointing to another data source
mediaPlayer.reset();
mediaPlayer.setAudioAttributes(new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build());
try {
// Set the data source to the mediaFile location
mediaPlayer.setDataSource(activeAudio.getFileLocation());
} catch (IOException e) {
e.printStackTrace();
stopSelf();
}
mediaPlayer.prepareAsync();
}
public void playMedia() {
if (!mediaPlayer.isPlaying()) {
mediaPlayer.start();
}
buildNotification(PlaybackStatus.PLAYING);
Intent intent = new Intent("PLAYBACK_STARTED");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
private void stopMedia() {
if (mediaPlayer == null) return;
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
}
public void pauseMedia() {
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();
resumePosition = mediaPlayer.getCurrentPosition();
}
buildNotification(PlaybackStatus.PAUSED);
Intent intent = new Intent("PLAYBACK_PAUSED");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
private void resumeMedia() {
if (!mediaPlayer.isPlaying()) {
mediaPlayer.seekTo(resumePosition);
mediaPlayer.start();
buildNotification(PlaybackStatus.PLAYING);
Intent intent = new Intent("PLAYBACK_STARTED");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
}
/**
* ACTION_AUDIO_BECOMING_NOISY -- change in audio outputs
*/
private BroadcastReceiver becomingNoisyReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//pause audio on ACTION_AUDIO_BECOMING_NOISY
pauseMedia();
buildNotification(PlaybackStatus.PAUSED);
}
};
private void registerBecomingNoisyReceiver() {
//register after getting audio focus
IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
registerReceiver(becomingNoisyReceiver, intentFilter);
}
/**
* Handle PhoneState changes
*/
private void callStateListener() {
// Get the telephony manager
telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
//Starting listening for PhoneState changes
phoneStateListener = new PhoneStateListener() {
#Override
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
//if at least one call exists or the phone is ringing
//pause the MediaPlayer
case TelephonyManager.CALL_STATE_OFFHOOK:
case TelephonyManager.CALL_STATE_RINGING:
if (mediaPlayer != null) {
pauseMedia();
ongoingCall = true;
}
break;
case TelephonyManager.CALL_STATE_IDLE:
// Phone idle. Start playing.
if (mediaPlayer != null) {
if (ongoingCall) {
ongoingCall = false;
resumeMedia();
}
}
break;
}
}
};
// Register the listener with the telephony manager
// Listen for changes to the device call state.
telephonyManager.listen(phoneStateListener,
PhoneStateListener.LISTEN_CALL_STATE);
}
/**
* MediaSession and Notification actions
*/
private void initMediaSession() throws RemoteException {
if (mediaSessionManager != null) return; //mediaSessionManager exists
mediaSessionManager = (MediaSessionManager) getSystemService(Context.MEDIA_SESSION_SERVICE);
// Create a new MediaSession
mediaSession = new MediaSessionCompat(getApplicationContext(), "AudioPlayer");
//Get MediaSessions transport controls
transportControls = mediaSession.getController().getTransportControls();
//set MediaSession -> ready to receive media commands
mediaSession.setActive(true);
//indicate that the MediaSession handles transport control commands
// through its MediaSessionCompat.Callback.
mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
//Set mediaSession's MetaData
updateMetaData();
// Attach Callback to receive MediaSession updates
mediaSession.setCallback(new MediaSessionCompat.Callback() {
// Implement callbacks
#Override
public void onPlay() {
super.onPlay();
resumeMedia();
}
#Override
public void onPause() {
super.onPause();
pauseMedia();
}
});
}
private void updateMetaData() {
fetchBitmapOfAlbum();
// Update the current metadata
mediaSession.setMetadata(new MediaMetadataCompat.Builder()
.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, albumArtBitmap)
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, "")
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, activeAudio.getAlbumName())
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, activeAudio.getTrackName())
.build());
}
private Target target = new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
albumArtBitmap = bitmap;
}
#Override
public void onBitmapFailed(Exception e, Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
};
private void fetchBitmapOfAlbum() {
Picasso.get().load(activeAudio.getAlbumArt())
.placeholder(R.drawable.rotate_animation)
.error(R.drawable.ic_blank)
.into(target);
}
private void buildNotification(PlaybackStatus playbackStatus) {
int notificationAction = android.R.drawable.ic_media_pause;//needs to be initialized
PendingIntent play_pauseAction = null;
//Build a new notification according to the current state of the MediaPlayer
if (playbackStatus == PlaybackStatus.PLAYING) {
notificationAction = android.R.drawable.ic_media_pause;
//create the pause action
play_pauseAction = playbackAction(1);
} else if (playbackStatus == PlaybackStatus.PAUSED) {
notificationAction = android.R.drawable.ic_media_play;
//create the play action
play_pauseAction = playbackAction(0);
}
fetchBitmapOfAlbum(); //replace with your own image
String channelId = "";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
channelId = "APP_MUSIC";
}
// Create a new Notification
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, channelId)
// Hide the timestamp
.setShowWhen(false)
// Set the Notification style
.setStyle(new androidx.media.app.NotificationCompat.MediaStyle()
// Attach our MediaSession token
.setMediaSession(mediaSession.getSessionToken())
// Show our playback controls in the compat view
.setShowActionsInCompactView(0, 1, 2))
// Set the Notification color
.setColor(ContextCompat.getColor(this.getApplicationContext(), R.color.colorAccent))
// Set the large and small icons
.setLargeIcon(albumArtBitmap)
.setSmallIcon(R.drawable.ic_stat_notifications)
// Set Notification content information
.setContentText(activeAudio.getTrackName())
.setTicker(activeAudio.getAlbumName() + "-" + activeAudio.getTrackName())
.setOngoing(true)
.setContentTitle(activeAudio.getAlbumName())
.setContentInfo(activeAudio.getTrackName())
// Add playback actions
.addAction(notificationAction, "pause", play_pauseAction)
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).notify(NOTIFICATION_ID, notificationBuilder.build());
}
private PendingIntent playbackAction(int actionNumber) {
Intent playbackAction = new Intent(this, MediaPlayerService.class);
switch (actionNumber) {
case 0:
// Play
playbackAction.setAction(ACTION_PLAY);
return PendingIntent.getService(this, actionNumber, playbackAction, 0);
case 1:
// Pause
playbackAction.setAction(ACTION_PAUSE);
return PendingIntent.getService(this, actionNumber, playbackAction, 0);
default:
break;
}
return null;
}
private void removeNotification() {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(NOTIFICATION_ID);
}
private void handleIncomingActions(Intent playbackAction) {
if (playbackAction == null || playbackAction.getAction() == null) return;
String actionString = playbackAction.getAction();
if (actionString.equalsIgnoreCase(ACTION_PLAY)) {
transportControls.play();
} else if (actionString.equalsIgnoreCase(ACTION_PAUSE)) {
transportControls.pause();
}
}
/**
* Play new Audio
*/
private BroadcastReceiver playNewAudio = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//Get the new media index form SharedPreferences
audioIndex = new StorageUtil(getApplicationContext()).loadAudioIndex();
if (audioIndex != -1 && audioIndex < audioList.size()) {
//index is in a valid range
activeAudio = audioList.get(audioIndex);
} else {
stopSelf();
}
//A PLAY_NEW_AUDIO action received
//reset mediaPlayer to play the new Audio
stopMedia();
mediaPlayer.reset();
initMediaPlayer();
updateMetaData();
buildNotification(PlaybackStatus.PLAYING);
}
};
private void register_playNewAudio() {
//Register playNewMedia receiver
IntentFilter filter = new IntentFilter(MainActivity.Broadcast_PLAY_NEW_AUDIO);
registerReceiver(playNewAudio, filter);
}
public boolean isPlaying(){
return mediaPlayer.isPlaying();
}
public void setShuffle(){
if(shuffle) shuffle=false;
else shuffle=true;
StorageUtil storage = new StorageUtil(getApplicationContext());
storage.storeShuffleRepeat("Shuffle", shuffle);
}
public void setRepeat(){
if(repeat) repeat=false;
else repeat=true;
StorageUtil storage = new StorageUtil(getApplicationContext());
storage.storeShuffleRepeat("Repeat", repeat);
}
}
Add the service to your manifest
<service android:name=".service.MediaPlayerService" />
Bind the service in MainActivity and declare methods to call the service
public class MainActivity {
private MediaPlayerService player;
boolean serviceBound = false;
public static final String Broadcast_PLAY_NEW_AUDIO = "pkg_name.PlayNewAudio";
//Binding this Client to the AudioPlayer Service
private ServiceConnection serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
MediaPlayerService.LocalBinder binder = (MediaPlayerService.LocalBinder) service;
player = binder.getService();
serviceBound = true;
}
#Override
public void onServiceDisconnected(ComponentName name) {
serviceBound = false;
}
};
// Call this method to play track
public void playAudio(int audioIndex, ArrayList<PlayableTrack> updatedList) {
//Check is service is active
audioList = updatedList;
if (!serviceBound) {
Intent playerIntent = new Intent(this, MediaPlayerService.class);
startService(playerIntent);
bindService(playerIntent, serviceConnection, Context.BIND_AUTO_CREATE);
} else {
//Service is active
//Send a broadcast to the service -> PLAY_NEW_AUDIO
Intent broadcastIntent = new Intent(Broadcast_PLAY_NEW_AUDIO);
sendBroadcast(broadcastIntent);
}
}
// Additional methods for control
public void start() {
player.playMedia();
}
public void pause() {
player.pauseMedia();
}
public boolean isPlaying() {
if (player != null && serviceBound) {
return player.isPlaying();
}
return false;
}
}
This is my solution:
1- first add a music file in raw folder (in my case coffee.mp3)
2- create a class named BackgroundSoundService that extends Service:
public class BackgroundSoundService extends Service {
MediaPlayer mediaPlayer;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
mediaPlayer = MediaPlayer.create(this, R.raw.coffee);
mediaPlayer.setLooping(true); // Set looping
mediaPlayer.setVolume(100, 100);
}
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent.getAction().equals("PLAY")) {
if(mediaPlayer.isPlaying() == false) {
mediaPlayer.start();
Toast.makeText(getApplicationContext(), "Music Is Playing", Toast.LENGTH_SHORT).show();
}
}
if (intent.getAction().equals("STOP")) {
this.stopService(intent);
Toast.makeText(getApplicationContext(), "Music Stopped", Toast.LENGTH_SHORT).show();
}
return startId;
}
#Override
public void onDestroy() {
mediaPlayer.stop();
mediaPlayer.release();
}
#Override
public void onLowMemory() {
}
}
do not forget to define this service in application tag of android manifest.xml:
<service android:name=".BackgroundSoundService" />
3- for play button in your activity use this code:
btnPlay.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String action = "PLAY";
Intent myService = new Intent(MainActivity.this, BackgroundSoundService.class);
myService.setAction(action);
startService(myService);
}
});
3- for stop button in your activity use this code:
btnStop.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String action = "STOP";
Intent myService = new Intent(MainActivity.this, BackgroundSoundService.class);
myService.setAction(action);
startService(myService);
}
});
I have created a simple android app with two activity which play music in background service, the app will pause the music if app is not visible to the user (for example user pressed home button).
you can see it in my github for more details.