Back to project page PlayTunes.
The source code is released under:
Copyright (c) 2014, Impeccable Labs, LLC. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions ar...
If you think the Android project PlayTunes listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package com.ideabag.playtunes.service; //from w w w . ja v a 2s .c o m import java.util.ArrayList; import android.annotation.SuppressLint; import android.app.Service; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.media.AudioManager; import android.os.Binder; import android.os.IBinder; import android.support.v4.app.Fragment; import com.google.android.gms.analytics.HitBuilders; import com.google.android.gms.analytics.Tracker; import com.ideabag.playtunes.R; import com.ideabag.playtunes.database.MediaQuery; import com.ideabag.playtunes.media.AudioFocusHelper; import com.ideabag.playtunes.media.MusicFocusable; import com.ideabag.playtunes.media.MusicIntentReceiver; import com.ideabag.playtunes.media.PlaylistMediaPlayer; import com.ideabag.playtunes.media.PlaylistMediaPlayer.PlaybackListener; import com.ideabag.playtunes.util.GAEvent; import com.ideabag.playtunes.util.GAEvent.AudioControls; import com.ideabag.playtunes.util.TrackerSingleton; import com.ideabag.playtunes.util.GAEvent.Categories; public class MusicPlayerService extends Service implements MusicFocusable { public static final String ACTION_PLAY = "com.ideabag.playtunes.PLAY"; public static final String ACTION_PLAY_OR_PAUSE = "com.ideabag.playtunes.PLAY_PAUSE"; public static final String ACTION_PAUSE = "com.ideabag.playtunes.PAUSE"; public static final String ACTION_NEXT = "com.ideabag.playtunes.NEXT"; public static final String ACTION_BACK = "com.ideabag.playtunes.BACK"; public static final String ACTION_CLOSE = "com.ideabag.playtunes.CLOSE"; private static final String PREF_KEY_NOWPLAYING_CLASS = "now_playing_class"; private static final String PREF_KEY_NOWPLAYING_NAME = "now_playing_name"; private static final String PREF_KEY_NOWPLAYING_ID = "now_playing_media_id"; private static final String PREF_KEY_NOWPLAYING_QUERY = "now_playing_media_query"; private static final String PREF_KEY_NOWPLAYING_POSITION = "now_playing_position"; private static final String PREF_KEY_NOWPLAYING_SHUFFLE = "now_playing_shuffle"; private static final String PREF_KEY_NOWPLAYING_LOOP = "now_playing_loop"; private Tracker mTracker; @SuppressWarnings("unused") private static final String TAG = "MusicPlayerService"; private PlaylistMediaPlayer MediaPlayer; private PlaybackNotification Notification; private LockscreenManager Lockscreen; private AudioManager mAudioManager; private SharedPreferences mSharedPrefs; AudioFocusHelper mAudioFocusHelper = null; // All used to save and restore what the user is playing across app open/close public String CURRENT_MEDIA_ID = null; public Class < ? extends Fragment > mPlaylistFragmentClass; public String mPlaylistMediaID; public String mPlaylistName; boolean isServiceStarted = false; private MusicPlayerService self; private ArrayList< PlaybackListener > ChangedListeners = new ArrayList< PlaybackListener >(); BroadcastReceiver NotificationActionReceiver = new BroadcastReceiver() { @Override public void onReceive( Context context, Intent intent ) { String action = intent.getAction(); if ( action.equals( ACTION_PLAY_OR_PAUSE ) ) { if ( null != MediaPlayer ) { if ( MediaPlayer.isPlaying() ) { mTracker.send( new HitBuilders.EventBuilder() .setCategory( intent.hasExtra( Categories.LOCKSCREEN ) ? Categories.LOCKSCREEN : Categories.NOTIFICATION ) .setAction( AudioControls.ACTION_PAUSE ) .build()); pause(); } else { mTracker.send( new HitBuilders.EventBuilder() .setCategory( intent.hasExtra( Categories.LOCKSCREEN ) ? Categories.LOCKSCREEN : Categories.NOTIFICATION ) .setAction( AudioControls.ACTION_PLAY ) .build()); play(); } } } else if ( action.equals( ACTION_NEXT ) ) { mTracker.send( new HitBuilders.EventBuilder() .setCategory( intent.hasExtra( Categories.LOCKSCREEN ) ? Categories.LOCKSCREEN : Categories.NOTIFICATION ) .setAction( AudioControls.ACTION_NEXT ) .build()); next(); } else if ( action.equals( ACTION_CLOSE ) ) { mTracker.send( new HitBuilders.EventBuilder() .setCategory( intent.hasExtra( Categories.LOCKSCREEN ) ? Categories.LOCKSCREEN : Categories.NOTIFICATION ) .setAction( GAEvent.Notification.ACTION_CLOSE ) .build()); if ( isServiceStarted ) { isServiceStarted = false; self.stopSelf(); } } else if ( action.equals( ACTION_BACK ) ) { mTracker.send( new HitBuilders.EventBuilder() .setCategory( intent.hasExtra( Categories.LOCKSCREEN ) ? Categories.LOCKSCREEN : Categories.NOTIFICATION ) .setAction( AudioControls.ACTION_PREV ) .build()); MediaPlayer.back(); } } }; @SuppressWarnings("unchecked") @SuppressLint("NewApi") @Override public void onCreate() { super.onCreate(); self = this; MediaPlayer = new PlaylistMediaPlayer( getBaseContext() ); Notification = new PlaybackNotification( getBaseContext() ); Lockscreen = new LockscreenManager( getBaseContext() ); mSharedPrefs = this.getSharedPreferences( getString( R.string.prefs_file ), Context.MODE_PRIVATE ); mTracker = TrackerSingleton.getDefaultTracker( this ); // IntentFilter mNotificationIntentFilter = new IntentFilter(); mNotificationIntentFilter.addAction( ACTION_PLAY_OR_PAUSE ); mNotificationIntentFilter.addAction( ACTION_NEXT ); mNotificationIntentFilter.addAction( ACTION_CLOSE ); mNotificationIntentFilter.addAction( ACTION_BACK ); registerReceiver( NotificationActionReceiver, mNotificationIntentFilter ); mAudioManager = ( AudioManager ) getBaseContext().getSystemService( Context.AUDIO_SERVICE ); mAudioManager.registerMediaButtonEventReceiver( new ComponentName( getBaseContext(), MusicIntentReceiver.class ) ); mAudioFocusHelper = new AudioFocusHelper( getApplicationContext(), this ); String mMediaQueryJSONString = mSharedPrefs.getString( PREF_KEY_NOWPLAYING_QUERY, null ); int mPlaylistPosition = mSharedPrefs.getInt( PREF_KEY_NOWPLAYING_POSITION, 0 ); boolean mIsShuffling = mSharedPrefs.getBoolean( PREF_KEY_NOWPLAYING_SHUFFLE, false ); int looping = mSharedPrefs.getInt( PREF_KEY_NOWPLAYING_LOOP, PlaylistMediaPlayer.LOOP_NO ); mPlaylistMediaID = mSharedPrefs.getString( PREF_KEY_NOWPLAYING_ID, null ); mPlaylistName = mSharedPrefs.getString( PREF_KEY_NOWPLAYING_NAME, "" ); String classNameString = mSharedPrefs.getString( PREF_KEY_NOWPLAYING_CLASS, "" ); try { mPlaylistFragmentClass = (Class<? extends Fragment>) Class.forName( classNameString ); } catch (ClassNotFoundException e) { e.printStackTrace(); } MediaPlayer.setPlaybackListener( MediaPlayerListener ); if ( mMediaQueryJSONString != null ) { MediaQuery mMediaQuery = new MediaQuery( mMediaQueryJSONString ); if ( mMediaQuery != null ) { MediaPlayer.setPlaylistQuery( mMediaQuery ); MediaPlayer.setPlaylistPosition( mPlaylistPosition ); MediaPlayer.setLooping( looping ); MediaPlayer.setShuffle( mIsShuffling ); } } if ( !isServiceStarted ) { startService( new Intent( this, MusicPlayerService.class ) ); isServiceStarted = true; } } @Override public void onDestroy() { super.onDestroy(); mAudioFocusHelper.abandonFocus(); Notification.remove(); unregisterReceiver( NotificationActionReceiver ); mAudioManager.unregisterMediaButtonEventReceiver( new ComponentName( getBaseContext(), MusicIntentReceiver.class ) ); ChangedListeners.clear(); Lockscreen.remove(); // Save state if ( MediaPlayer.getPlaylistQuery() != null ) { SharedPreferences.Editor edit = mSharedPrefs.edit(); edit.putString( PREF_KEY_NOWPLAYING_QUERY, MediaPlayer.getPlaylistQuery().toJSONString() ); edit.putInt( PREF_KEY_NOWPLAYING_POSITION, MediaPlayer.getPlaylistPosition() ); edit.putString( PREF_KEY_NOWPLAYING_CLASS, mPlaylistFragmentClass.getName() ); edit.putString( PREF_KEY_NOWPLAYING_NAME, mPlaylistName ); edit.putString( PREF_KEY_NOWPLAYING_ID, mPlaylistMediaID ); edit.putInt( PREF_KEY_NOWPLAYING_LOOP, MediaPlayer.getLoopState() ); edit.putBoolean( PREF_KEY_NOWPLAYING_SHUFFLE, MediaPlayer.isShuffling() ); edit.commit(); } // Clean up Media Player MediaPlayer.pause(); MediaPlayer.destroy(); // Remove Notification (if showing) } public class MusicPlayerServiceBinder extends Binder { public MusicPlayerService getService() { return MusicPlayerService.this; } } // // Service Binder & Public APIs // // // private final IBinder mBinder = new MusicPlayerServiceBinder(); @Override public IBinder onBind( Intent intent ) { return mBinder; } public void setPlaylist( MediaQuery query, String playlistName, Class < ? extends Fragment > fragmentClass, String playlistMediaID ) { if ( null != MediaPlayer ) { MediaPlayer.setPlaylistQuery( query ); this.mPlaylistFragmentClass = fragmentClass; this.mPlaylistMediaID = playlistMediaID; this.mPlaylistName = playlistName; SharedPreferences.Editor edit = mSharedPrefs.edit(); edit.putString( PREF_KEY_NOWPLAYING_ID, playlistMediaID ); edit.putString( PREF_KEY_NOWPLAYING_NAME, playlistName ); edit.putString( PREF_KEY_NOWPLAYING_CLASS, fragmentClass.getName() ); edit.commit(); } } // // rectifyPlaylist is called when the content of the query changes // for instance, when a song is removed from a playlist or deleted from the device. // // - The goal is to switch to the new query cursor, but to retain play position // - It is possible that the currently playing song is not in the new query // - It is possible that the currently playing song is in a new position in the query // // public void rectifyPlaylist( MediaQuery query, String playlistName, Class < ? extends Fragment > fragmentClass, String playlistMediaID ) { if ( null != MediaPlayer ) { MediaPlayer.setPlaylistQuery( query ); this.mPlaylistFragmentClass = fragmentClass; this.mPlaylistMediaID = playlistMediaID; this.mPlaylistName = playlistName; SharedPreferences.Editor edit = mSharedPrefs.edit(); edit.putString( PREF_KEY_NOWPLAYING_ID, playlistMediaID ); edit.putString( PREF_KEY_NOWPLAYING_NAME, playlistName ); edit.putString( PREF_KEY_NOWPLAYING_CLASS, fragmentClass.getName() ); //edit. //android.util.Log.i( TAG, "" + playlistMediaID + " " + fragmentClass.getName() ); edit.commit(); } } public void setPlaylistPosition( int position ) { if ( null != MediaPlayer ) { MediaPlayer.setPlaylistPosition( position ); } } public void setSeekPosition( int position ) { if ( null != MediaPlayer ) { MediaPlayer.setSeekPosition( position ); } } public void next() { if ( null != MediaPlayer ) { MediaPlayer.nextTrack(); } } public void prev() { if ( null != MediaPlayer ) { MediaPlayer.back(); } } public void play() { if ( null != MediaPlayer && !MediaPlayer.isPlaying() ) { MediaPlayer.play(); } } public void pause() { if ( null != MediaPlayer ) { MediaPlayer.pause(); } } public void setLooping( int repeat ) { MediaPlayer.setLooping( repeat ); } public void setShuffle( boolean isShuffling ) { MediaPlayer.setShuffle( isShuffling ); } // // The PlaylistMediaPlayer creates a PlaybackListener interface and uses it as a callback for changes in playback. // In MusicPlayerService, we use that interface both to update the notification and to rebroadcast that // callback information to as many clients as are interested. // // We use the number of PlaybackListeners as a way to determine if the Service is detached or not. // // public void addPlaybackListener( PlaybackListener listener ) { this.ChangedListeners.add( listener ); // When we get a PlaybackListener, we immediately fire all the callbacks with the current state information if ( null != MediaPlayer ) { listener.onTrackChanged( MediaPlayer.getCurrentMediaID() ); listener.onLoopingChanged( MediaPlayer.getLoopState() ); listener.onShuffleChanged( MediaPlayer.isShuffling() ); listener.onDurationChanged( MediaPlayer.getTrackPosition() , MediaPlayer.getTrackDuration() ); if ( MediaPlayer.isPlaying() ) { listener.onPlay(); } else { listener.onPause(); } } // // Listeners are added when an interested Activity UI is shown, so we remove // the notification in this case. // Notification.remove(); } public void removePlaybackListener( PlaybackListener listener ) { this.ChangedListeners.remove( listener ); // // Listeners are removed when an interested Activity or Fragment UI is hidden, so we either // show the notification in this case and update it with the currently playing song // or destroy the service if no music is playing. // if ( this.ChangedListeners.size() == 0 ) { if ( MediaPlayer.isPlaying() ) { Notification.setMediaID( CURRENT_MEDIA_ID ); Notification.play(); Notification.show(); } else if ( isServiceStarted ) { isServiceStarted = false; this.stopSelf(); } } } PlaybackListener MediaPlayerListener = new PlaybackListener() { // // No null check because null is a meaningful value in this situation // @Override public void onTrackChanged( String media_id ) { CURRENT_MEDIA_ID = media_id; int count = ChangedListeners.size(); for ( int x = 0; x < count; x++ ) { ChangedListeners.get( x ).onTrackChanged( media_id ); } Lockscreen.setMediaID( media_id, MediaPlayer.hasPreviousTrack(), MediaPlayer.hasNextTrack() ); Notification.setMediaID( media_id ); if ( MediaPlayer.isPlaying() ) { mAudioFocusHelper.requestFocus(); } } @Override public void onPlaylistDone() { mAudioFocusHelper.abandonFocus(); int count = ChangedListeners.size(); for ( int x = 0; x < count; x++ ) { ChangedListeners.get( x ).onPlaylistDone(); } Lockscreen.remove(); Notification.remove(); } @Override public void onLoopingChanged( int loop ) { int count = ChangedListeners.size(); for ( int x = 0; x < count; x++ ) { ChangedListeners.get( x ).onLoopingChanged( loop ); } } @Override public void onShuffleChanged( boolean isShuffling ) { int count = ChangedListeners.size(); for ( int x = 0; x < count; x++ ) { ChangedListeners.get( x ).onShuffleChanged( isShuffling ); } } @Override public void onPlay() { int count = ChangedListeners.size(); mAudioFocusHelper.requestFocus(); for ( int x = 0; x < count; x++ ) { ChangedListeners.get( x ).onPlay(); } if ( null == Lockscreen || !Lockscreen.ready() ) { Lockscreen = new LockscreenManager( getBaseContext() ); Lockscreen.setMediaID( CURRENT_MEDIA_ID, MediaPlayer.hasPreviousTrack(), MediaPlayer.hasNextTrack() ); } Lockscreen.play(); Notification.play(); } @Override public void onPause() { int count = ChangedListeners.size(); for ( int x = 0; x < count; x++ ) { ChangedListeners.get( x ).onPause(); } Lockscreen.pause(); Notification.pause(); } @Override public void onDurationChanged( int position, int duration ) { int count = ChangedListeners.size(); for ( int x = 0; x < count; x++ ) { ChangedListeners.get( x ).onDurationChanged( position, duration ); } } }; @Override public void onGainedAudioFocus() { // TODO: MediaPlayer.stopVolumeDucking(); // } @Override public void onLostAudioFocus( boolean canDuck ) { if ( !canDuck ) { Lockscreen.remove(); //destroyRemoteControlClient(); pause(); } else { MediaPlayer.startVolumeDucking(); } } }