diff --git a/README.md b/README.md index f7f6816..c02a75e 100755 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # RadioPlayerService -[![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-RadioPlayerService-green.svg?style=flat)](http://android-arsenal.com/details/1/2168) + Android service library which uses AAC Player. Ready to use Radio Player Service. (Android Background Player Service) @@ -41,7 +41,7 @@ repositories { ``` dependencies { - compile 'com.github.iammert:RadioPlayerService:efe3b5420b' + compile 'com.github.thedude61636:RadioPlayerService:1.5' } ``` @@ -69,6 +69,10 @@ mRadioManager.connect(); //Invoke it #onDestroy mRadioManager.disconnect(); ``` +```java +//Cancel notification and stop the player +mRadioManager.cancelNotification(); +``` Play and pause radio like ```java @@ -113,8 +117,6 @@ RadioManager.with(getActivity()).unregisterListener(this); Demo project will help you to understand implementation. -## Error Case Solutions ## -If you get UnsatisfiedLinkError on some android devices, please go through the [link](https://medium.com/mobiwise-blog/unsatisfiedlinkerror-problem-on-some-android-devices-b77f2f83837d#.c1vlmowal) that I explained how to solve it. ## TODO LIST## diff --git a/app/build.gradle b/app/build.gradle index 670825e..8b30fdc 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,13 +1,13 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 22 - buildToolsVersion "23.0.1" + compileSdkVersion 23 + buildToolsVersion "23.0.3" defaultConfig { applicationId "co.mobiwise.myapplication" - minSdkVersion 16 - targetSdkVersion 22 + minSdkVersion 15 + targetSdkVersion 23 versionCode 1 versionName "1.0" @@ -29,5 +29,4 @@ repositories { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile project(':library') - //compile 'com.github.iammert:RadioPlayerService:9a0cc8dc94' } diff --git a/build.gradle b/build.gradle index 3fb8c55..6300e4c 100755 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.2.3' + classpath 'com.android.tools.build:gradle:2.1.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/library/build.gradle b/library/build.gradle index 61a6ff1..7cde427 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -1,18 +1,15 @@ apply plugin: 'com.android.library' android { - compileSdkVersion 22 - buildToolsVersion "22.0.1" + compileSdkVersion 23 + buildToolsVersion "23.0.3" defaultConfig { - minSdkVersion 16 - targetSdkVersion 22 + minSdkVersion 15 + targetSdkVersion 23 versionCode 1 versionName "1.0" - ndk { - abiFilters "armeabi-v7a", "x86", "armeabi", "mips" - } } buildTypes { @@ -25,6 +22,6 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:appcompat-v7:22.2.0' + compile 'com.android.support:appcompat-v7:23.3.0' compile files('libs/aacdecoder-android-0.8.jar') } diff --git a/library/libs/aacdecoder-android-0.8.jar b/library/libs/aacdecoder-android-0.8.jar index dde2d0d..cd9a60c 100755 Binary files a/library/libs/aacdecoder-android-0.8.jar and b/library/libs/aacdecoder-android-0.8.jar differ diff --git a/library/src/main/java/co/mobiwise/library/media/MediaManager.java b/library/src/main/java/co/mobiwise/library/media/MediaManager.java index d94fd75..ec50cef 100644 --- a/library/src/main/java/co/mobiwise/library/media/MediaManager.java +++ b/library/src/main/java/co/mobiwise/library/media/MediaManager.java @@ -16,150 +16,159 @@ */ public class MediaManager { - /** - * Context - */ - private Context mContext; - - /** - * MediaPlayerService - */ - private static MediaPlayerService mService; - - /** - * Boolean check for service connection - */ - private boolean isServiceConnected = false; - - /** - * Listeners wil lbe added here if - * service is not connected yet. - * After service connected, we will empty this queue - * by adding them to service listeners list. - */ - private List mMediaListenerQueue; - - /** - * MediaManager - */ - private static MediaManager instance = null; - - /** - * if play requested before service connection, we set this value as true. - */ - private boolean isPlayRequested = false; - - /** - * current stream URL - */ - private String mStreamURL; - - /** - * private construtor - */ - private MediaManager(Context mContext) { - this.mContext = mContext; - mMediaListenerQueue = new ArrayList<>(); - } - - /** - * Singleton instance - * - * @return - */ - public static MediaManager with(Context context) { - if (instance == null) - instance = new MediaManager(context); - return instance; - } - - public static MediaPlayerService getService() { - return mService; - } - - public void play(String mStreamURL) { - this.mStreamURL = mStreamURL; - if (isServiceConnected) { - mService.play(mStreamURL); - } else { - isPlayRequested = true; + /** + * Context + */ + private Context mContext; + + /** + * MediaPlayerService + */ + private static MediaPlayerService mService; + + /** + * Boolean check for service connection + */ + private boolean isServiceConnected = false; + + /** + * Listeners wil lbe added here if + * service is not connected yet. + * After service connected, we will empty this queue + * by adding them to service listeners list. + */ + private List mMediaListenerQueue; + + /** + * MediaManager + */ + private static MediaManager instance = null; + + /** + * if play requested before service connection, we set this value as true. + */ + private boolean isPlayRequested = false; + + /** + * current stream URL + */ + private String mStreamURL; + + /** + * private construtor + */ + private MediaManager(Context mContext) { + this.mContext = mContext; + mMediaListenerQueue = new ArrayList<>(); } - } - - public void pause() { - if (isServiceConnected) - mService.pause(); - } - - public void seekTo(int duration) { - if (isServiceConnected) - mService.seekTo(duration); - } - - public boolean isPlaying() { - if (isServiceConnected) - return mService.isPlaying(); - return false; - } - - public void updateNotification(String singerName, String songName, int smallArt, int bigArt) { - if (mService != null) - mService.updateNotification(singerName, songName, smallArt, bigArt); - } - - public void updateNotification(String singerName, String songName, int smallArt, Bitmap bigArt) { - if (mService != null) - mService.updateNotification(singerName, songName, smallArt, bigArt); - } - - public void registerListener(MediaListener mediaListener) { - if (isServiceConnected) - mService.registerMediaListener(mediaListener); - else - mMediaListenerQueue.add(mediaListener); - } - - /** - * Connect service - */ - public void connect() { - Intent intent = new Intent(mContext, MediaPlayerService.class); - mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); - } - - /** - * Disconnect service - */ - public void disconnect() { - mContext.unbindService(mServiceConnection); - } - - /** - * Connection - */ - private ServiceConnection mServiceConnection = new ServiceConnection() { - - @Override - public void onServiceConnected(ComponentName arg0, IBinder binder) { - - Log.v("TEST", "SERVICE CONNECTED."); - mService = ((MediaPlayerService.LocalBinder) binder).getService(); - isServiceConnected = true; - if (isPlayRequested) { - play(mStreamURL); - isPlayRequested = false; - } - - if (!mMediaListenerQueue.isEmpty()) { - for (MediaListener mediaListener : mMediaListenerQueue) - registerListener(mediaListener); - } + + /** + * Singleton instance + * + * @return + */ + public static MediaManager with(Context context) { + if (instance == null) + instance = new MediaManager(context); + return instance; + } + + public static MediaPlayerService getService() { + return mService; + } + + public void play(String mStreamURL) { + this.mStreamURL = mStreamURL; + if (isServiceConnected) { + mService.play(mStreamURL); + } else { + isPlayRequested = true; + } + } + + public void pause() { + if (isServiceConnected) + mService.pause(); + } + + public void seekTo(int duration) { + if (isServiceConnected) + mService.seekTo(duration); + } + + public boolean isPlaying() { + if (isServiceConnected) + return mService.isPlaying(); + return false; + } + + public void updateNotification(String singerName, String songName, int smallArt, int bigArt) { + if (mService != null) + mService.updateNotification(singerName, songName, smallArt, bigArt); + } + + public void updateNotification(String singerName, String songName, int smallArt, Bitmap bigArt) { + if (mService != null) + mService.updateNotification(singerName, songName, smallArt, bigArt); } - @Override - public void onServiceDisconnected(ComponentName arg0) { + public void cancelNotification(){ + if (mService!=null){ + mService.cancelNotification(); + } } - }; + + public void registerListener(MediaListener mediaListener) { + if (isServiceConnected) + mService.registerMediaListener(mediaListener); + else + mMediaListenerQueue.add(mediaListener); + } + + /** + * Connect service + */ + public void connect() { + Intent intent = new Intent(mContext, MediaPlayerService.class); + mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); + } + + /** + * Disconnect service + */ + public void disconnect() { + mContext.unbindService(mServiceConnection); + } + + + + + /** + * Connection + */ + private ServiceConnection mServiceConnection = new ServiceConnection() { + + @Override + public void onServiceConnected(ComponentName arg0, IBinder binder) { + + Log.v("TEST", "SERVICE CONNECTED."); + mService = ((MediaPlayerService.LocalBinder) binder).getService(); + isServiceConnected = true; + if (isPlayRequested) { + play(mStreamURL); + isPlayRequested = false; + } + + if (!mMediaListenerQueue.isEmpty()) { + for (MediaListener mediaListener : mMediaListenerQueue) + registerListener(mediaListener); + } + } + + @Override + public void onServiceDisconnected(ComponentName arg0) { + } + }; } diff --git a/library/src/main/java/co/mobiwise/library/media/MediaPlayerService.java b/library/src/main/java/co/mobiwise/library/media/MediaPlayerService.java index a5f41e4..b18d48c 100644 --- a/library/src/main/java/co/mobiwise/library/media/MediaPlayerService.java +++ b/library/src/main/java/co/mobiwise/library/media/MediaPlayerService.java @@ -1,7 +1,7 @@ package co.mobiwise.library.media; +import android.annotation.TargetApi; import android.app.Notification; -import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Intent; @@ -14,7 +14,8 @@ import android.os.IBinder; import android.os.PowerManager; import android.support.annotation.Nullable; -import android.widget.RemoteViews; +import android.support.v4.app.NotificationManagerCompat; +import android.support.v7.app.NotificationCompat; import java.io.IOException; import java.util.ArrayList; @@ -28,7 +29,7 @@ public class MediaPlayerService extends Service implements MediaPlayer.OnPreparedListener, MediaPlayer.OnSeekCompleteListener, - MediaPlayer.OnCompletionListener{ + MediaPlayer.OnCompletionListener { /** @@ -81,7 +82,8 @@ public enum State { /** * Notification manager */ - private NotificationManager mNotificationManager; + private NotificationManagerCompat notificationManagerCompat; + /** * Stop action. If another radioplayer will start.It needs @@ -133,13 +135,10 @@ public int onStartCommand(Intent intent, int flags, int startId) { String action = intent.getAction(); - if(action.equals(NOTIFICATION_INTENT_CANCEL)){ - if(isPlaying()) - stop(); - mNotificationManager.cancel(NOTIFICATION_ID); - } - else if(action.equals(NOTIFICATION_INTENT_PLAY_PAUSE)){ - if(isPlaying()) + if (action.equals(NOTIFICATION_INTENT_CANCEL)) { + cancelNotification(); + } else if (action.equals(NOTIFICATION_INTENT_PLAY_PAUSE)) { + if (isPlaying()) pause(); else play(mStreamURL); @@ -149,6 +148,7 @@ else if(action.equals(NOTIFICATION_INTENT_PLAY_PAUSE)){ return START_NOT_STICKY; } + /** * OnCreate */ @@ -156,7 +156,7 @@ else if(action.equals(NOTIFICATION_INTENT_PLAY_PAUSE)){ public void onCreate() { super.onCreate(); - mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + notificationManagerCompat = NotificationManagerCompat.from(this); mediaListenerList = new ArrayList<>(); initializeMediaPlayer(); } @@ -245,6 +245,7 @@ private void resetMediaPlayer() { /** * Called when mediaplayer prepared to play + * * @param mp */ @Override @@ -256,6 +257,7 @@ public void onPrepared(MediaPlayer mp) { /** * Seek completed + * * @param mp */ @Override @@ -266,6 +268,7 @@ public void onSeekComplete(MediaPlayer mp) { /** * End of file + * * @param mp */ @Override @@ -310,7 +313,7 @@ private void notifyPlayerLoading() { /** * Build notification */ - private void buildNotification(){ + private void buildNotification() { /** * Intents @@ -322,82 +325,79 @@ private void buildNotification(){ /** * Pending intents */ - PendingIntent playPausePending = PendingIntent.getService(this,0,intentPlayPause,0); - PendingIntent openPending = PendingIntent.getService(this,0,intentOpenPlayer,0); - PendingIntent cancelPending = PendingIntent.getService(this,0,intentCancel,0); + PendingIntent playPausePending = PendingIntent.getService(this, 0, intentPlayPause, 0); + PendingIntent openPending = PendingIntent.getService(this, 0, intentOpenPlayer, 0); + PendingIntent cancelPending = PendingIntent.getService(this, 0, intentCancel, 0); /** * Remote view for normal view */ - RemoteViews mNotificationTemplate = new RemoteViews(this.getPackageName(), R.layout.notification); - Notification.Builder notificationBuilder = new Notification.Builder(this); + NotificationCompat.Builder notificationCompatBuilder = new NotificationCompat.Builder(this); /** * set small notification texts and image */ - if(artImage == null) - artImage = BitmapFactory.decodeResource(getResources(),R.drawable.default_art); - - mNotificationTemplate.setTextViewText(R.id.notification_line_one, singerName); - mNotificationTemplate.setTextViewText(R.id.notification_line_two, songName); - mNotificationTemplate.setImageViewResource(R.id.notification_play, isPlaying() ? R.drawable.btn_playback_pause : R.drawable.btn_playback_play); - mNotificationTemplate.setImageViewBitmap(R.id.notification_image, artImage); - - /** - * OnClickPending intent for collapsed notification - */ - mNotificationTemplate.setOnClickPendingIntent(R.id.notification_collapse, cancelPending); - mNotificationTemplate.setOnClickPendingIntent(R.id.notification_play, playPausePending); + if (artImage == null) + artImage = BitmapFactory.decodeResource(getResources(), R.drawable.default_art); /** * Create notification instance */ - Notification notification = notificationBuilder + notificationCompatBuilder .setSmallIcon(smallImage) + .setLargeIcon(artImage) + .setContentTitle(songName) + .setContentText(singerName) .setContentIntent(openPending) - .setPriority(Notification.PRIORITY_DEFAULT) - .setContent(mNotificationTemplate) - .setUsesChronometer(true) - .build(); - notification.flags = Notification.FLAG_ONGOING_EVENT; + .setDeleteIntent(cancelPending) + .setOngoing(isPlaying()) + .setWhen(0) + .addAction(isPlaying() ? R.drawable.ic_pause : R.drawable.ic_play_arrow, isPlaying() ? getResources().getString(R.string.pause) : getResources().getString(R.string.play), playPausePending) + .setStyle(new NotificationCompat.MediaStyle().setShowActionsInCompactView(0)) + ; + /** * Expanded notification */ - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - - RemoteViews mExpandedView = new RemoteViews(this.getPackageName(),R.layout.notification_expanded); - - mExpandedView.setTextViewText(R.id.notification_line_one, singerName); - mExpandedView.setTextViewText(R.id.notification_line_two, songName); - mExpandedView.setImageViewResource(R.id.notification_expanded_play, isPlaying() ? R.drawable.btn_playback_pause : R.drawable.btn_playback_play); - mExpandedView.setImageViewBitmap(R.id.notification_image, artImage); + applyLollipopFunctionality(notificationCompatBuilder); - mExpandedView.setOnClickPendingIntent(R.id.notification_collapse, cancelPending); - mExpandedView.setOnClickPendingIntent(R.id.notification_expanded_play, playPausePending); + if (notificationManagerCompat != null) + notificationManagerCompat.notify(NOTIFICATION_ID, notificationCompatBuilder.build()); + } - notification.bigContentView = mExpandedView; + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + private void applyLollipopFunctionality(NotificationCompat.Builder notificationCompatBuilder) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + notificationCompatBuilder + .setCategory(Notification.CATEGORY_TRANSPORT) + .setVisibility(Notification.VISIBILITY_PUBLIC); } - - if(mNotificationManager != null) - mNotificationManager.notify(NOTIFICATION_ID, notification); } - public void updateNotification(String singerName, String songName, int smallImage, int artImage){ + public void updateNotification(String singerName, String songName, int smallImage, int artImage) { this.singerName = singerName; this.songName = songName; this.smallImage = smallImage; - this.artImage = BitmapFactory.decodeResource(getResources(),artImage); + this.artImage = BitmapFactory.decodeResource(getResources(), artImage); buildNotification(); } - public void updateNotification(String singerName, String songName, int smallImage, Bitmap artImage){ + public void updateNotification(String singerName, String songName, int smallImage, Bitmap artImage) { this.singerName = singerName; this.songName = songName; this.smallImage = smallImage; this.artImage = artImage; buildNotification(); } + + public void cancelNotification() { + if (isPlaying()) + stop(); + notificationManagerCompat.cancel(NOTIFICATION_ID); + + } + } diff --git a/library/src/main/java/co/mobiwise/library/radio/IRadioManager.java b/library/src/main/java/co/mobiwise/library/radio/IRadioManager.java index 0a588eb..d410017 100755 --- a/library/src/main/java/co/mobiwise/library/radio/IRadioManager.java +++ b/library/src/main/java/co/mobiwise/library/radio/IRadioManager.java @@ -28,4 +28,7 @@ public interface IRadioManager { void updateNotification(String singerName, String songName, int smallArt, Bitmap bigArt); + void enableNotification(boolean isEnabled); + + void cancelNotification(); } diff --git a/library/src/main/java/co/mobiwise/library/radio/RadioManager.java b/library/src/main/java/co/mobiwise/library/radio/RadioManager.java index 5d8278e..59a858b 100755 --- a/library/src/main/java/co/mobiwise/library/radio/RadioManager.java +++ b/library/src/main/java/co/mobiwise/library/radio/RadioManager.java @@ -16,202 +16,227 @@ */ public class RadioManager implements IRadioManager { - /** - * Logging enable/disable - */ - private static boolean isLogging = false; - - /** - * Singleton - */ - private static RadioManager instance = null; - - /** - * RadioPlayerService - */ - private static RadioPlayerService mService; - - /** - * Context - */ - private Context mContext; - - /** - * Listeners - */ - private List mRadioListenerQueue; - - /** - * Service connected/Disconnected lock - */ - private boolean isServiceConnected; - - /** - * Private constructor because of Singleton pattern - * @param mContext - */ - private RadioManager(Context mContext) { - this.mContext = mContext; - mRadioListenerQueue = new ArrayList<>(); - isServiceConnected = false; - } - - /** - * Singleton - * @param mContext - * @return - */ - public static RadioManager with(Context mContext) { - if (instance == null) - instance = new RadioManager(mContext); - return instance; - } - - /** - * get current service instance - * @return RadioPlayerService - */ - public static RadioPlayerService getService(){ - return mService; - } - - /** - * Start Radio Streaming - * @param streamURL - */ - @Override - public void startRadio(String streamURL) { - mService.play(streamURL); - } - - /** - * Stop Radio Streaming - */ - @Override - public void stopRadio() { - mService.stop(); - } - - /** - * Check if radio is playing - * @return - */ - @Override - public boolean isPlaying() { - log("IsPlaying : " + mService.isPlaying()); - return mService.isPlaying(); - } - - /** - * Register listener to listen radio service actions - * @param mRadioListener - */ - @Override - public void registerListener(RadioListener mRadioListener) { - if (isServiceConnected) - mService.registerListener(mRadioListener); - else - mRadioListenerQueue.add(mRadioListener); - } - - /** - * Unregister listeners - * @param mRadioListener - */ - @Override - public void unregisterListener(RadioListener mRadioListener) { - log("Register unregistered."); - mService.unregisterListener(mRadioListener); - } + /** + * Logging enable/disable + */ + private static boolean isLogging = false; + + /** + * Singleton + */ + private static RadioManager instance = null; + + /** + * RadioPlayerService + */ + private static RadioPlayerService mService; + + /** + * Context + */ + private Context mContext; + + /** + * Listeners + */ + private List mRadioListenerQueue; + + /** + * Service connected/Disconnected lock + */ + private boolean isServiceConnected; + + /** + * notification enabled/disabled control + */ + private boolean isEnabled = true; + /** + * Connection + */ + private ServiceConnection mServiceConnection = new ServiceConnection() { - /** - * Set/Unset Logging - * @param logging - */ @Override - public void setLogging(boolean logging) { - isLogging = logging; - } + public void onServiceConnected(ComponentName arg0, IBinder binder) { - /** - * Connect radio player service - */ - @Override - public void connect() { - log("Requested to connect service."); - Intent intent = new Intent(mContext, RadioPlayerService.class); - mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); - } + log("Service Connected."); - /** - * Disconnect radio player service - */ - @Override - public void disconnect() { - log("Service Disconnected."); - mContext.unbindService(mServiceConnection); - } + mService = ((RadioPlayerService.LocalBinder) binder).getService(); + mService.setLogging(isLogging); + isServiceConnected = true; + mService.enableNotification(isEnabled); - /** - * Update notification data - * @param singerName - * @param songName - * @param smallArt - * @param bigArt - */ - @Override - public void updateNotification(String singerName, String songName, int smallArt, int bigArt) { - if(mService != null) - mService.updateNotification(singerName, songName, smallArt, bigArt); + if (!mRadioListenerQueue.isEmpty()) { + for (RadioListener mRadioListener : mRadioListenerQueue) { + registerListener(mRadioListener); + mRadioListener.onRadioConnected(); + } + } } - /** - * Update notification data - * @param singerName - * @param songName - * @param smallArt - * @param bigArt - */ @Override - public void updateNotification(String singerName, String songName, int smallArt, Bitmap bigArt) { - if(mService != null) - mService.updateNotification(singerName, songName, smallArt, bigArt); - } - - /** - * Connection - */ - private ServiceConnection mServiceConnection = new ServiceConnection() { - - @Override - public void onServiceConnected(ComponentName arg0, IBinder binder) { - - log("Service Connected."); - - mService = ((RadioPlayerService.LocalBinder) binder).getService(); - mService.setLogging(isLogging); - isServiceConnected = true; - - if (!mRadioListenerQueue.isEmpty()) { - for (RadioListener mRadioListener : mRadioListenerQueue) { - registerListener(mRadioListener); - mRadioListener.onRadioConnected(); - } - } - } - - @Override - public void onServiceDisconnected(ComponentName arg0) { - } - }; - - /** - * Logger - * @param log - */ - private void log(String log) { - if (isLogging) - Log.v("RadioManager", "RadioManagerLog : " + log); + public void onServiceDisconnected(ComponentName arg0) { } + }; + + /** + * Private constructor because of Singleton pattern + * + * @param mContext + */ + private RadioManager(Context mContext) { + this.mContext = mContext; + mRadioListenerQueue = new ArrayList<>(); + isServiceConnected = false; + } + + /** + * Singleton + * + * @param mContext + * @return + */ + public static RadioManager with(Context mContext) { + if (instance == null) + instance = new RadioManager(mContext); + return instance; + } + + /** + * get current service instance + * + * @return RadioPlayerService + */ + public static RadioPlayerService getService() { + return mService; + } + + /** + * Start Radio Streaming + * + * @param streamURL + */ + @Override + public void startRadio(String streamURL) { + mService.play(streamURL); + } + + /** + * Stop Radio Streaming + */ + @Override + public void stopRadio() { + mService.stop(); + } + + /** + * Check if radio is playing + * + * @return + */ + @Override + public boolean isPlaying() { + log("IsPlaying : " + mService.isPlaying()); + return mService.isPlaying(); + } + + /** + * Register listener to listen radio service actions + * + * @param mRadioListener + */ + @Override + public void registerListener(RadioListener mRadioListener) { + if (isServiceConnected) + mService.registerListener(mRadioListener); + else + mRadioListenerQueue.add(mRadioListener); + } + + /** + * Unregister listeners + * + * @param mRadioListener + */ + @Override + public void unregisterListener(RadioListener mRadioListener) { + log("Register unregistered."); + mService.unregisterListener(mRadioListener); + } + + /** + * Set/Unset Logging + * + * @param logging + */ + @Override + public void setLogging(boolean logging) { + isLogging = logging; + } + + /** + * Connect radio player service + */ + @Override + public void connect() { + log("Requested to connect service."); + Intent intent = new Intent(mContext, RadioPlayerService.class); + mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); + } + + /** + * Disconnect radio player service + */ + @Override + public void disconnect() { + log("Service Disconnected."); + mContext.unbindService(mServiceConnection); + } + + /** + * Update notification data + * + * @param singerName + * @param songName + * @param smallArt + * @param bigArt + */ + @Override + public void updateNotification(String singerName, String songName, int smallArt, int bigArt) { + if (mService != null && isEnabled) + mService.updateNotification(singerName, songName, smallArt, bigArt); + } + + /** + * Update notification data + * + * @param singerName + * @param songName + * @param smallArt + * @param bigArt + */ + @Override + public void updateNotification(String singerName, String songName, int smallArt, Bitmap bigArt) { + if (mService != null && isEnabled) + mService.updateNotification(singerName, songName, smallArt, bigArt); + } + + @Override + public void enableNotification(boolean isEnabled) { + this.isEnabled = isEnabled; + } + + @Override + public void cancelNotification(){ + mService.cancelNotification(); + } + /** + * Logger + * + * @param log + */ + private void log(String log) { + if (isLogging) + Log.v("RadioManager", "RadioManagerLog : " + log); + } } diff --git a/library/src/main/java/co/mobiwise/library/radio/RadioPlayerService.java b/library/src/main/java/co/mobiwise/library/radio/RadioPlayerService.java index ca91125..df1d1cb 100755 --- a/library/src/main/java/co/mobiwise/library/radio/RadioPlayerService.java +++ b/library/src/main/java/co/mobiwise/library/radio/RadioPlayerService.java @@ -1,7 +1,7 @@ package co.mobiwise.library.radio; +import android.annotation.TargetApi; import android.app.Notification; -import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Intent; @@ -12,10 +12,11 @@ import android.os.Binder; import android.os.Build; import android.os.IBinder; +import android.support.v4.app.NotificationManagerCompat; +import android.support.v7.app.NotificationCompat; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import android.util.Log; -import android.widget.RemoteViews; import com.spoledge.aacdecoder.MultiPlayer; import com.spoledge.aacdecoder.PlayerCallback; @@ -31,73 +32,56 @@ */ public class RadioPlayerService extends Service implements PlayerCallback { + /** + * Stop action. If another mediaplayer will start.It needs + * to send broadcast to stop this service. + */ + public static final String ACTION_MEDIAPLAYER_STOP = "co.mobiwise.library.ACTION_STOP_MEDIAPLAYER"; /** * Notification action intent strings */ private static final String NOTIFICATION_INTENT_PLAY_PAUSE = "co.mobiwise.library.notification.radio.INTENT_PLAYPAUSE"; - private static final String NOTIFICATION_INTENT_CANCEL = "co.mobiwise.library.notification.radio.INTENT_CANCEL"; - private static final String NOTIFICATION_INTENT_OPEN_PLAYER = "co.mobiwise.library.notification.radio.INTENT_OPENPLAYER"; - - /** - * Notification current values - */ - private String singerName = ""; - private String songName = ""; - private int smallImage = R.drawable.default_art; - private Bitmap artImage; - /** * Notification ID */ private static final int NOTIFICATION_ID = 001; - /** * Logging control variable */ private static boolean isLogging = false; - + /** + * Binder + */ + public final IBinder mLocalBinder = new LocalBinder(); /** * Radio buffer and decode capacity(DEFAULT VALUES) */ private final int AUDIO_BUFFER_CAPACITY_MS = 800; private final int AUDIO_DECODE_CAPACITY_MS = 400; - /** * Stream url suffix */ private final String SUFFIX_PLS = ".pls"; private final String SUFFIX_RAM = ".ram"; private final String SUFFIX_WAX = ".wax"; - + List mListenerList; /** - * State enum for Radio Player state (IDLE, PLAYING, STOPPED, INTERRUPTED) + * Notification current values */ - public enum State { - IDLE, - PLAYING, - STOPPED, - } - - List mListenerList; - + private String singerName = ""; + private String songName = ""; + private int smallImage = R.drawable.default_art; + private Bitmap artImage; /** * Radio State */ private State mRadioState; - /** * Current radio URL */ private String mRadioUrl; - - /** - * Stop action. If another mediaplayer will start.It needs - * to send broadcast to stop this service. - */ - public static final String ACTION_MEDIAPLAYER_STOP = "co.mobiwise.library.ACTION_STOP_MEDIAPLAYER"; - /** * AAC Radio Player */ @@ -127,6 +111,12 @@ public enum State { */ private boolean isInterrupted; + /** + * Notification will be shown if this + * value set true; + */ + private boolean isNotificationEnabled = true; + /** * If play method is called repeatedly, AAC Decoder will be failed. * play and stop methods will be turned mLock = true when they called, @@ -134,31 +124,51 @@ public enum State { * @onRadioStarted and @onRadioStopped methods will be release lock. */ private boolean mLock; + PhoneStateListener phoneStateListener = new PhoneStateListener() { + @Override + public void onCallStateChanged(int state, String incomingNumber) { + if (state == TelephonyManager.CALL_STATE_RINGING) { - /** - * Notification manager - */ - private NotificationManager mNotificationManager; + /** + * Stop radio and set interrupted if it is playing on incoming call. + */ + if (isPlaying()) { + isInterrupted = true; + stop(); + } + + } else if (state == TelephonyManager.CALL_STATE_IDLE) { + + /** + * Keep playing if it is interrupted. + */ + if (isInterrupted) + play(mRadioUrl); + + } else if (state == TelephonyManager.CALL_STATE_OFFHOOK) { + + /** + * Stop radio and set interrupted if it is playing on outgoing call. + */ + if (isPlaying()) { + isInterrupted = true; + stop(); + } + } + super.onCallStateChanged(state, incomingNumber); + } + }; /** - * Binder + * Notification manager */ - public final IBinder mLocalBinder = new LocalBinder(); + private NotificationManagerCompat notificationManagerCompat; @Override public IBinder onBind(Intent intent) { return mLocalBinder; } - /** - * Binder - */ - public class LocalBinder extends Binder { - public RadioPlayerService getService() { - return RadioPlayerService.this; - } - } - /** * Service called * @@ -177,12 +187,7 @@ public int onStartCommand(Intent intent, int flags, int startId) { * IDLE, stop player and cancel notification */ if (action.equals(NOTIFICATION_INTENT_CANCEL)) { - if (isPlaying()) { - isClosedFromNotification = true; - stop(); - } - if (mNotificationManager != null) - mNotificationManager.cancel(NOTIFICATION_ID); + cancelNotification(); } /** * If play/pause action clicked on notification, @@ -212,12 +217,13 @@ public void onCreate() { getPlayer(); mTelephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE); - mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + notificationManagerCompat = NotificationManagerCompat.from(this); if (mTelephonyManager != null) mTelephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); } + /** * Play url if different from previous streaming url. * @@ -258,7 +264,8 @@ public void stop() { @Override public void playerStarted() { mRadioState = State.PLAYING; - buildNotification(); + if (isNotificationEnabled) + buildNotification(); mLock = false; notifyRadioStarted(); @@ -289,10 +296,13 @@ public void playerStopped(int i) { * If player stopped from notification then dont * call buildNotification(). */ - if (!isClosedFromNotification) - buildNotification(); - else + if (!isClosedFromNotification) { + if (isNotificationEnabled) { + buildNotification(); + } + } else { isClosedFromNotification = false; + } mLock = false; notifyRadioStopped(); @@ -309,6 +319,7 @@ public void playerException(Throwable throwable) { mLock = false; mRadioPlayer = null; getPlayer(); + notifyErrorOccured(); log("ERROR OCCURED."); } @@ -353,13 +364,12 @@ private void notifyRadioLoading() { } } - private void notifyErrorOccured(){ + private void notifyErrorOccured() { for (RadioListener mRadioListener : mListenerList) { mRadioListener.onError(); } } - /** * Return AAC player. If it is not initialized, creates and returns. * @@ -389,42 +399,6 @@ public java.net.URLStreamHandler createURLStreamHandler(String protocol) { return mRadioPlayer; } - PhoneStateListener phoneStateListener = new PhoneStateListener() { - @Override - public void onCallStateChanged(int state, String incomingNumber) { - if (state == TelephonyManager.CALL_STATE_RINGING) { - - /** - * Stop radio and set interrupted if it is playing on incoming call. - */ - if (isPlaying()) { - isInterrupted = true; - stop(); - } - - } else if (state == TelephonyManager.CALL_STATE_IDLE) { - - /** - * Keep playing if it is interrupted. - */ - if (isInterrupted) - play(mRadioUrl); - - } else if (state == TelephonyManager.CALL_STATE_OFFHOOK) { - - /** - * Stop radio and set interrupted if it is playing on outgoing call. - */ - if (isPlaying()) { - isInterrupted = true; - stop(); - } - - } - super.onCallStateChanged(state, incomingNumber); - } - }; - /** * Check supported suffix * @@ -499,59 +473,51 @@ private void buildNotification() { * Remote view for normal view */ - RemoteViews mNotificationTemplate = new RemoteViews(this.getPackageName(), R.layout.notification); - Notification.Builder notificationBuilder = new Notification.Builder(this); /** * set small notification texts and image */ - if (artImage == null) + NotificationCompat.Builder notificationCompatBuilder = new NotificationCompat.Builder(this); + if (artImage == null) { artImage = BitmapFactory.decodeResource(getResources(), R.drawable.default_art); - - mNotificationTemplate.setTextViewText(R.id.notification_line_one, singerName); - mNotificationTemplate.setTextViewText(R.id.notification_line_two, songName); - mNotificationTemplate.setImageViewResource(R.id.notification_play, isPlaying() ? R.drawable.btn_playback_pause : R.drawable.btn_playback_play); - mNotificationTemplate.setImageViewBitmap(R.id.notification_image, artImage); - - /** - * OnClickPending intent for collapsed notification - */ - mNotificationTemplate.setOnClickPendingIntent(R.id.notification_collapse, cancelPending); - mNotificationTemplate.setOnClickPendingIntent(R.id.notification_play, playPausePending); + } /** * Create notification instance */ - Notification notification = notificationBuilder + notificationCompatBuilder .setSmallIcon(smallImage) + .setLargeIcon(artImage) + .setContentTitle(songName) + .setContentText(singerName) .setContentIntent(openPending) - .setPriority(Notification.PRIORITY_DEFAULT) - .setContent(mNotificationTemplate) - .setUsesChronometer(true) - .build(); - notification.flags = Notification.FLAG_ONGOING_EVENT; + .setDeleteIntent(cancelPending) + .setOngoing(isPlaying()) + .setWhen(0) + .addAction(isPlaying() ? R.drawable.ic_pause : R.drawable.ic_play_arrow, isPlaying() ? getResources().getString(R.string.pause) : getResources().getString(R.string.play), playPausePending) + .setStyle(new NotificationCompat.MediaStyle().setShowActionsInCompactView(0)) + ; + /** * Expanded notification */ - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - RemoteViews mExpandedView = new RemoteViews(this.getPackageName(), R.layout.notification_expanded); + applyLollipopFunctionality(notificationCompatBuilder); - mExpandedView.setTextViewText(R.id.notification_line_one, singerName); - mExpandedView.setTextViewText(R.id.notification_line_two, songName); - mExpandedView.setImageViewResource(R.id.notification_expanded_play, isPlaying() ? R.drawable.btn_playback_pause : R.drawable.btn_playback_play); - mExpandedView.setImageViewBitmap(R.id.notification_image, artImage); - mExpandedView.setOnClickPendingIntent(R.id.notification_collapse, cancelPending); - mExpandedView.setOnClickPendingIntent(R.id.notification_expanded_play, playPausePending); + if (notificationManagerCompat != null) + notificationManagerCompat.notify(NOTIFICATION_ID, notificationCompatBuilder.build()); + } - notification.bigContentView = mExpandedView; + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + private void applyLollipopFunctionality(NotificationCompat.Builder notificationCompatBuilder) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + notificationCompatBuilder + .setCategory(Notification.CATEGORY_TRANSPORT) + .setVisibility(Notification.VISIBILITY_PUBLIC); } - - if (mNotificationManager != null) - mNotificationManager.notify(NOTIFICATION_ID, notification); } public void updateNotification(String singerName, String songName, int smallImage, int artImage) { @@ -559,17 +525,47 @@ public void updateNotification(String singerName, String songName, int smallImag this.songName = songName; this.smallImage = smallImage; this.artImage = BitmapFactory.decodeResource(getResources(), artImage); - buildNotification(); + if (isNotificationEnabled) + buildNotification(); } - public void updateNotification(String singerName, String songName, int smallImage, Bitmap artImage) { this.singerName = singerName; this.songName = songName; this.smallImage = smallImage; this.artImage = artImage; - buildNotification(); + if (isNotificationEnabled) + buildNotification(); + } + + public void cancelNotification() { + if (isPlaying()) { + isClosedFromNotification = true; + stop(); + } + if (notificationManagerCompat != null) + notificationManagerCompat.cancel(NOTIFICATION_ID); + } + + public void enableNotification(boolean isEnabled) { + isNotificationEnabled = isEnabled; } + /** + * State enum for Radio Player state (IDLE, PLAYING, STOPPED, INTERRUPTED) + */ + public enum State { + IDLE, + PLAYING, + STOPPED, + } + /** + * Binder + */ + public class LocalBinder extends Binder { + public RadioPlayerService getService() { + return RadioPlayerService.this; + } + } } diff --git a/library/src/main/jniLibs/armeabi-v7a/libaacdecoder.so b/library/src/main/jniLibs/armeabi-v7a/libaacdecoder.so index f2a3a70..610df1b 100755 Binary files a/library/src/main/jniLibs/armeabi-v7a/libaacdecoder.so and b/library/src/main/jniLibs/armeabi-v7a/libaacdecoder.so differ diff --git a/library/src/main/jniLibs/armeabi/libaacdecoder.so b/library/src/main/jniLibs/armeabi/libaacdecoder.so index fefd827..7f03217 100755 Binary files a/library/src/main/jniLibs/armeabi/libaacdecoder.so and b/library/src/main/jniLibs/armeabi/libaacdecoder.so differ diff --git a/library/src/main/jniLibs/mips/libaacdecoder.so b/library/src/main/jniLibs/mips/libaacdecoder.so index a7facfa..36c3b46 100755 Binary files a/library/src/main/jniLibs/mips/libaacdecoder.so and b/library/src/main/jniLibs/mips/libaacdecoder.so differ diff --git a/library/src/main/jniLibs/x86/libaacdecoder.so b/library/src/main/jniLibs/x86/libaacdecoder.so index 7a0e292..ecfbe95 100755 Binary files a/library/src/main/jniLibs/x86/libaacdecoder.so and b/library/src/main/jniLibs/x86/libaacdecoder.so differ diff --git a/library/src/main/res/drawable/btn_notification_collapse.png b/library/src/main/res/drawable/btn_notification_collapse.png deleted file mode 100644 index 5b04d33..0000000 Binary files a/library/src/main/res/drawable/btn_notification_collapse.png and /dev/null differ diff --git a/library/src/main/res/drawable/btn_playback_pause.png b/library/src/main/res/drawable/btn_playback_pause.png deleted file mode 100644 index 6b435bb..0000000 Binary files a/library/src/main/res/drawable/btn_playback_pause.png and /dev/null differ diff --git a/library/src/main/res/drawable/btn_playback_play.png b/library/src/main/res/drawable/btn_playback_play.png deleted file mode 100644 index df8a2ca..0000000 Binary files a/library/src/main/res/drawable/btn_playback_play.png and /dev/null differ diff --git a/library/src/main/res/drawable/ic_close.xml b/library/src/main/res/drawable/ic_close.xml new file mode 100644 index 0000000..c820415 --- /dev/null +++ b/library/src/main/res/drawable/ic_close.xml @@ -0,0 +1,9 @@ + + + diff --git a/library/src/main/res/drawable/ic_pause.xml b/library/src/main/res/drawable/ic_pause.xml new file mode 100644 index 0000000..ce3899d --- /dev/null +++ b/library/src/main/res/drawable/ic_pause.xml @@ -0,0 +1,9 @@ + + + diff --git a/library/src/main/res/drawable/ic_play_arrow.xml b/library/src/main/res/drawable/ic_play_arrow.xml new file mode 100644 index 0000000..86f1ad4 --- /dev/null +++ b/library/src/main/res/drawable/ic_play_arrow.xml @@ -0,0 +1,9 @@ + + + diff --git a/library/src/main/res/values/strings.xml b/library/src/main/res/values/strings.xml index 49fc91e..2d58dee 100644 --- a/library/src/main/res/values/strings.xml +++ b/library/src/main/res/values/strings.xml @@ -1,3 +1,5 @@ library + Play + Pause