Last active
July 6, 2017 13:13
-
-
Save talhahasanzia/ae937bfd918dcead51058aa6e0713a53 to your computer and use it in GitHub Desktop.
Media player service using : https://www.sitepoint.com/a-step-by-step-guide-to-building-an-android-audio-player-app/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<manifest xmlns:android="http://schemas.android.com/apk/res/android" | |
package="com.example.mediaplayer"> | |
<uses-permission android:name="android.permission.INTERNET"/> | |
<permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/> | |
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> | |
<uses-permission android:name="android.permission.READ_PHONE_STATE"/> | |
<application | |
android:allowBackup="true" | |
android:icon="@mipmap/ic_launcher" | |
android:label="@string/app_name" | |
android:roundIcon="@mipmap/ic_launcher_round" | |
android:supportsRtl="true" | |
android:theme="@style/AppTheme"> | |
<activity android:name=".MainActivity"> | |
<intent-filter> | |
<action android:name="android.intent.action.MAIN"/> | |
<category android:name="android.intent.category.LAUNCHER"/> | |
</intent-filter> | |
</activity> | |
<service | |
android:name=".MediaPlayerService" | |
android:enabled="true" | |
android:exported="true"> | |
</service> | |
</application> | |
</manifest> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.mediaplayer; | |
import java.io.Serializable; | |
public class Audio implements Serializable | |
{ | |
private String data; | |
private String title; | |
private String album; | |
private String artist; | |
public Audio(String data, String title, String album, String artist) { | |
this.data = data; | |
this.title = title; | |
this.album = album; | |
this.artist = artist; | |
} | |
public String getData() { | |
return data; | |
} | |
public void setData(String data) { | |
this.data = data; | |
} | |
public String getTitle() { | |
return title; | |
} | |
public void setTitle(String title) { | |
this.title = title; | |
} | |
public String getAlbum() { | |
return album; | |
} | |
public void setAlbum(String album) { | |
this.album = album; | |
} | |
public String getArtist() { | |
return artist; | |
} | |
public void setArtist(String artist) { | |
this.artist = artist; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.mediaplayer; | |
import android.content.ComponentName; | |
import android.content.ContentResolver; | |
import android.content.Context; | |
import android.content.Intent; | |
import android.content.ServiceConnection; | |
import android.database.Cursor; | |
import android.net.Uri; | |
import android.os.Bundle; | |
import android.os.IBinder; | |
import android.provider.MediaStore; | |
import android.support.v7.app.AppCompatActivity; | |
import android.view.View; | |
import android.widget.Button; | |
import android.widget.Toast; | |
import java.util.ArrayList; | |
public class MainActivity extends AppCompatActivity | |
{ | |
Button play, pause, stop, toggle; | |
ArrayList<Audio> audioList; | |
private MediaPlayerService player; | |
boolean serviceBound = false; | |
@Override | |
protected void onCreate( Bundle savedInstanceState ) | |
{ | |
super.onCreate( savedInstanceState ); | |
setContentView( R.layout.activity_main ); | |
play= (Button) findViewById( R.id.play ); | |
pause= (Button) findViewById( R.id.pause ); | |
stop=(Button) findViewById( R.id.stop ); | |
toggle= (Button) findViewById( R.id.toggle ); | |
play.setOnClickListener( new View.OnClickListener() | |
{ | |
@Override | |
public void onClick( View view ) | |
{ | |
playAudio( R.raw.shape_of_you ); | |
} | |
} ); | |
pause.setOnClickListener( new View.OnClickListener() | |
{ | |
@Override | |
public void onClick( View view ) | |
{ | |
if(serviceBound && player.isPlaying() && !player.isStopped()) | |
player.pauseMedia(); | |
} | |
} ); | |
stop.setOnClickListener( new View.OnClickListener() | |
{ | |
@Override | |
public void onClick( View view ) | |
{ | |
if(serviceBound && player.isPlaying() && !player.isStopped() ) | |
player.stopMedia(); | |
} | |
} ); | |
toggle.setOnClickListener( new View.OnClickListener() | |
{ | |
@Override | |
public void onClick( View view ) | |
{ | |
player.setRepeating( !player.isRepeating() ); | |
} | |
} ); | |
/*loadAudio(this); | |
//play the first audio in the ArrayList | |
playAudio(audioList.get(0).getData());*/ | |
} | |
//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; | |
Toast.makeText(MainActivity.this, "Service Bound", Toast.LENGTH_SHORT).show(); | |
} | |
@Override | |
public void onServiceDisconnected(ComponentName name) { | |
serviceBound = false; | |
} | |
}; | |
private void playAudio(int media) { | |
//Check is service is active | |
if (!serviceBound) { | |
Intent playerIntent = new Intent(this, MediaPlayerService.class); | |
playerIntent.putExtra("media", media); | |
playerIntent.putExtra( "play",true ); | |
startService(playerIntent); | |
bindService(playerIntent, serviceConnection, Context.BIND_AUTO_CREATE); | |
} else { | |
//Service is active | |
//Send media with BroadcastReceiver | |
if(!player.isStopped()) | |
player.playMedia(); | |
else | |
{ | |
Intent playerIntent = new Intent(this, MediaPlayerService.class); | |
playerIntent.putExtra("media", media); | |
playerIntent.putExtra( "play",true ); | |
startService(playerIntent); | |
bindService(playerIntent, serviceConnection, Context.BIND_AUTO_CREATE); | |
} | |
} | |
} | |
@Override | |
public void onSaveInstanceState(Bundle savedInstanceState) { | |
savedInstanceState.putBoolean("ServiceState", serviceBound); | |
super.onSaveInstanceState(savedInstanceState); | |
} | |
@Override | |
public void onRestoreInstanceState(Bundle savedInstanceState) { | |
super.onRestoreInstanceState(savedInstanceState); | |
serviceBound = savedInstanceState.getBoolean("ServiceState"); | |
} | |
@Override | |
protected void onDestroy() | |
{ | |
super.onDestroy(); | |
if ( serviceBound ) | |
{ | |
unbindService( serviceConnection ); | |
//service is active | |
player.stopSelf(); | |
} | |
} | |
private void loadAudio(Context context) { | |
ContentResolver contentResolver = context.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) { | |
audioList = new ArrayList<>(); | |
while (cursor.moveToNext()) { | |
String data = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA)); | |
String title = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.TITLE)); | |
String album = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM)); | |
String artist = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST)); | |
// Save to audioList | |
audioList.add(new Audio(data, title, album, artist)); | |
} | |
} | |
cursor.close(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.mediaplayer; | |
import android.app.Service; | |
import android.content.Context; | |
import android.content.Intent; | |
import android.content.res.AssetFileDescriptor; | |
import android.media.AudioManager; | |
import android.media.MediaPlayer; | |
import android.os.Binder; | |
import android.os.IBinder; | |
import android.telephony.PhoneStateListener; | |
import android.telephony.TelephonyManager; | |
import android.util.Log; | |
import java.io.IOException; | |
public class MediaPlayerService extends Service implements MediaPlayer.OnCompletionListener, | |
MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener, MediaPlayer.OnSeekCompleteListener, | |
MediaPlayer.OnInfoListener, MediaPlayer.OnBufferingUpdateListener, | |
AudioManager.OnAudioFocusChangeListener | |
{ | |
// Binder given to clients | |
private final IBinder iBinder = new LocalBinder(); | |
private AudioManager audioManager; | |
private MediaPlayer mediaPlayer; | |
//path to the audio file | |
private int mediaFile; | |
private int resumePosition; | |
//Handle incoming phone calls | |
private boolean ongoingCall = false; | |
private PhoneStateListener phoneStateListener; | |
private TelephonyManager telephonyManager; | |
private boolean repeating = false; | |
private boolean playing = false; | |
private boolean paused = false; | |
private boolean stopped = false; | |
private boolean preparing = false; | |
private boolean playOnInit = false; | |
boolean wasPlaying =false; | |
boolean FOCUS=false; | |
@Override | |
public int onStartCommand( Intent intent, int flags, int startId ) | |
{ | |
try | |
{ | |
//An audio file is passed to the service through putExtra(); | |
mediaFile = intent.getExtras().getInt( "media" ); | |
playOnInit = intent.getExtras().getBoolean( "play" ); | |
} | |
catch ( NullPointerException e ) | |
{ | |
stopSelf(); | |
} | |
//Request audio focus | |
if ( requestAudioFocus() == false ) | |
{ | |
//Could not gain focus | |
stopSelf(); | |
} | |
if ( mediaFile != -1 && mediaFile != 0 ) | |
{ | |
initMediaPlayer(); | |
} | |
return super.onStartCommand( intent, flags, startId ); | |
} | |
@Override | |
public void onDestroy() | |
{ | |
super.onDestroy(); | |
if ( mediaPlayer != null ) | |
{ | |
stopMedia(); | |
mediaPlayer.release(); | |
} | |
removeAudioFocus(); | |
} | |
@Override | |
public IBinder onBind( Intent intent ) | |
{ | |
return iBinder; | |
} | |
private void initMediaPlayer() | |
{ | |
mediaPlayer = new MediaPlayer(); | |
//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.setLooping( isRepeating() ); | |
mediaPlayer.setAudioStreamType( AudioManager.STREAM_MUSIC ); | |
// Set the data source to the mediaFile location | |
AssetFileDescriptor afd = getResources().openRawResourceFd(mediaFile); | |
try | |
{ | |
mediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); | |
} | |
catch ( IOException e ) | |
{ | |
e.printStackTrace(); | |
stopSelf(); | |
} | |
mediaPlayer.prepareAsync(); | |
preparing = true; | |
} | |
@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(); | |
playing = false; | |
paused = false; | |
stopped = true; | |
} | |
//Handle errors | |
@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. | |
preparing = false; | |
if ( playOnInit ) | |
{ | |
playMedia(); | |
playOnInit = !playOnInit; | |
} | |
callStateListener(); | |
} | |
@Override | |
public boolean onInfo( MediaPlayer mp, int what, int extra ) | |
{ | |
//Invoked to communicate some info. | |
return false; | |
} | |
@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 ) | |
{ | |
} | |
else if ( wasPlaying ) | |
{ | |
wasPlaying =false; | |
if(paused) | |
playMedia(); | |
} | |
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: | |
// 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(); | |
wasPlaying =true; | |
} | |
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; | |
} | |
} | |
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 ); | |
} | |
public boolean isRepeating() | |
{ | |
return repeating; | |
} | |
public void setRepeating( boolean repeating ) | |
{ | |
this.repeating = repeating; | |
if ( mediaPlayer != null ) | |
{ | |
mediaPlayer.setLooping( repeating ); | |
} | |
} | |
public void playMedia() | |
{ | |
if ( !mediaPlayer.isPlaying() ) | |
{ | |
mediaPlayer.start(); | |
playing = true; | |
paused = false; | |
stopped = false; | |
wasPlaying =false; | |
} | |
} | |
public void stopMedia() | |
{ | |
if ( mediaPlayer == null ) | |
{ | |
return; | |
} | |
if ( mediaPlayer.isPlaying() ) | |
{ | |
mediaPlayer.stop(); | |
} | |
playing = false; | |
paused = false; | |
stopped = true; | |
} | |
public void pauseMedia() | |
{ | |
if ( mediaPlayer.isPlaying() ) | |
{ | |
mediaPlayer.pause(); | |
resumePosition = mediaPlayer.getCurrentPosition(); | |
playing = false; | |
paused = true; | |
stopped = false; | |
} | |
} | |
public void resumeMedia() | |
{ | |
if ( !mediaPlayer.isPlaying() ) | |
{ | |
mediaPlayer.seekTo( resumePosition ); | |
mediaPlayer.start(); | |
playing = true; | |
paused = false; | |
stopped = false; | |
} | |
} | |
//Handle incoming phone calls | |
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 ); | |
} | |
public boolean isPlaying() | |
{ | |
return playing; | |
} | |
public boolean isPaused() | |
{ | |
return paused; | |
} | |
public boolean isStopped() | |
{ | |
return stopped; | |
} | |
public boolean isPreparing() | |
{ | |
return preparing; | |
} | |
public class LocalBinder extends Binder | |
{ | |
public MediaPlayerService getService() | |
{ | |
return MediaPlayerService.this; | |
} | |
} | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.mediaplayer; | |
import android.content.Context; | |
/** | |
* Created by tzia on 06-Jul-17. | |
*/ | |
public class Utils | |
{ | |
public static int getFileFromAssetFolder(String fileName, Context context){ | |
return context.getResources().getIdentifier( fileName, "raw", context.getPackageName() ); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment