Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lock Screen Player Controls and Meta Data

I'm trying to use MediaSessionCompat in order to add lock screen player controls and meta data for my app. Everything I tried doesn't work. The lock screen doesn't show any controls or meta data while playing. Please see my current code below and any help is appreciated.

StreamService.java:

public class StreamService extends Service implements MediaPlayer.OnCuePointReceivedListener, MediaPlayer.OnStateChangedListener,
        MediaPlayer.OnInfoListener, AudioManager.OnAudioFocusChangeListener {

    private WifiManager.WifiLock wifiLock;
    private static String LOG_TAG = "StreamService";
    public static final String BROADCAST_PLAYER_STATE = "com.test.BROADCAST_PLAYER_STATE";
    public static final String BROADCAST_PLAYER_META = "com.test.BROADCAST_PLAYER_META";
    public static final String BROADCAST_PLAYER_ALBUM = "com.test.BROADCAST_PLAYER_ALBUM";
    public static final int NOTIFICATION_ID = 999999;
    private MediaSessionCompat mediaSession;
    private boolean audioInterrupted = false;

    public StreamService() {
    }

    @Override
    public void onCreate(){
        super.onCreate();

        setupMediaPlayer();

        setupMediaSession();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public boolean onUnbind(Intent intent){
        releasePlayer();
        return false;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_NOT_STICKY;
    }

    private void setupMediaPlayer() {
        // Recreate player
        Bundle playerSettings = (BrandedApplication.getContext().getmTritonPlayer() == null) ? null : BrandedApplication.getContext().getmTritonPlayer().getSettings();
        Bundle inputSettings = createPlayerSettings();

        if (!Utility.bundleEquals(inputSettings, playerSettings)) {
            releasePlayer();
            createPlayer(inputSettings);
        }

        // Start the playback
        play();
    }

    private void setupMediaSession() {
        ComponentName receiver = new ComponentName(getPackageName(), RemoteReceiver.class.getName());
        mediaSession = new MediaSessionCompat(this, "StreamService", receiver, null);
        mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
                MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
        mediaSession.setPlaybackState(new PlaybackStateCompat.Builder()
                .setState(PlaybackStateCompat.STATE_PAUSED, 0, 0)
                .setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PAUSE)
                .build());
        mediaSession.setMetadata(new MediaMetadataCompat.Builder()
                .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, "Test Artist")
                .putString(MediaMetadataCompat.METADATA_KEY_ALBUM, "Test Album")
                .putString(MediaMetadataCompat.METADATA_KEY_TITLE, "Test Track Name")
                .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, 10000)
                .putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART,
                        BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
                //.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, "Test Artist")
                .build());

        AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        audioManager.requestAudioFocus(new AudioManager.OnAudioFocusChangeListener() {
            @Override
            public void onAudioFocusChange(int focusChange) {
                // Ignore
            }
        }, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);

        mediaSession.setActive(true);
    }

    synchronized private void play() {
        audioInterrupted = false;
        BrandedApplication.getContext().getmTritonPlayer().play();
        if(wifiLock != null) {
            wifiLock.acquire();
        }

        if(mediaSession != null) {
            mediaSession.setPlaybackState(new PlaybackStateCompat.Builder()
                    .setState(PlaybackStateCompat.STATE_PLAYING, 0, 1.0f)
                    .setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE).build());
        }
    }

    synchronized private void stop() {
        BrandedApplication.getContext().getmTritonPlayer().stop();
        if(wifiLock != null) {
            wifiLock.release();
        }

        if(mediaSession != null) {
            mediaSession.setPlaybackState(new PlaybackStateCompat.Builder()
                    .setState(PlaybackStateCompat.STATE_PAUSED, 0, 0.0f)
                    .setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE).build());
        }
    }

    private void createPlayer(Bundle settings)
    {
        BrandedApplication.getContext().setmTritonPlayer(new TritonPlayer(this, settings));
        wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))
                .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");
        AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC,
                AudioManager.AUDIOFOCUS_GAIN);
        BrandedApplication.getContext().getmTritonPlayer().setOnCuePointReceivedListener(this);
        BrandedApplication.getContext().getmTritonPlayer().setOnInfoListener(this);
        BrandedApplication.getContext().getmTritonPlayer().setOnStateChangedListener(this);
    }


    protected void releasePlayer() {
        if (BrandedApplication.getContext().getmTritonPlayer() != null) {
            if(BrandedApplication.getContext().isPlaying()) {
                stop();
            }
            BrandedApplication.getContext().getmTritonPlayer().release();
            BrandedApplication.getContext().setmTritonPlayer(null);
        }
        stopForeground(true);
    }

    protected Bundle createPlayerSettings() {
        // Player Settings
        Bundle settings = new Bundle();
        // AAC
        settings.putString(TritonPlayer.SETTINGS_STATION_MOUNT, getResources().getString(R.string.station_stream_mount) + "AAC");
        // MP3
        //settings.putString(TritonPlayer.SETTINGS_STATION_MOUNT, mountID);
        settings.putString(TritonPlayer.SETTINGS_STATION_BROADCASTER, getResources().getString(R.string.app_name));
        settings.putString(TritonPlayer.SETTINGS_STATION_NAME, getResources().getString(R.string.app_name));
        return settings;
    }

    @Override
    public void onCuePointReceived(MediaPlayer mediaPlayer, Bundle bundle) {
        //System.out.println("TRITON PLAYER BUNDLE " + bundle);
        String trackName = "";
        String artistName = "";
        if(bundle != null) {
            if(bundle.containsKey("cue_title") && bundle.containsKey("track_artist_name")) {
                if (!bundle.getString("cue_title").isEmpty()) {
                    trackName = bundle.getString("cue_title");
                }
                if (!bundle.getString("track_artist_name").isEmpty()) {
                    artistName = bundle.getString("track_artist_name");
                }
            }
        }
        // broadcast out the meta data
        Intent i = new Intent(BROADCAST_PLAYER_META);
        i.putExtra("trackName", trackName);
        i.putExtra("artistName", artistName);
        sendBroadcast(i);

        // send notification and start as foreground service
        PendingIntent pi = PendingIntent.getActivity(getApplicationContext(), 0, new Intent(getApplicationContext(), MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
        Bitmap icon = BitmapFactory.decodeResource(getResources(),
                R.drawable.logo);
        String tickerString = "";
        String contentString = "Playing";
        if(!artistName.isEmpty() && !trackName.isEmpty()) {
            tickerString = artistName + " - " + trackName;
            contentString += ": " + artistName + " - " + trackName;
        }

        Intent pauseIntent = new Intent(BROADCAST_PLAYER_PAUSE);
    PendingIntent pausePendingIntent = PendingIntent.getBroadcast(this, 0, pauseIntent, 0);

    NotificationCompat.Builder notification = new NotificationCompat.Builder(this)
            .setContentTitle(getResources().getString(R.string.app_name))
            .setTicker(tickerString)
            .setContentText(contentString)
            .setSmallIcon(R.drawable.ic_launcher)
            //.setAutoCancel(true)
            //.setLargeIcon(
            //        Bitmap.createScaledBitmap(icon, 128, 128, false))
            .addAction(R.drawable.ic_media_pause, "Pause", pausePendingIntent)
            .setContentIntent(pi)
            .setStyle(new android.support.v7.app.NotificationCompat.MediaStyle()
                    //.setShowActionsInCompactView(0)
                    .setMediaSession(mediaSession.getSessionToken()))
            .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
            .setOngoing(true);
    //notification.setPriority(Notification.PRIORITY_MIN);
    notification.setPriority(Notification.PRIORITY_DEFAULT);
    startForeground(NOTIFICATION_ID, notification.build());
    }

    @Override
    public void onInfo(MediaPlayer mediaPlayer, int i, int i1) {

    }

    @Override
    public void onStateChanged(MediaPlayer mediaPlayer, int state) {
        Log.i(LOG_TAG, "onStateChanged: " + TritonPlayer.debugStateToStr(state));
        // broadcast out the player state
        Intent i = new Intent(BROADCAST_PLAYER_STATE);
        i.putExtra("state", state);
        sendBroadcast(i);
    }

    @Override
    public void onAudioFocusChange(int focusChange) {
        switch (focusChange) {
            case AudioManager.AUDIOFOCUS_GAIN:
                // resume playback
                System.out.println("AUDIO FOCUS GAIN");
                if(audioInterrupted) {
                    audioInterrupted = false;
                    if (BrandedApplication.getContext().getmTritonPlayer() == null) {
                        setupMediaPlayer();
                    } else if (!BrandedApplication.getContext().isPlaying()) {
                        setupMediaPlayer();
                    }
                }
                break;

            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
            case AudioManager.AUDIOFOCUS_LOSS:
                System.out.println("AUDIO FOCUS LOSS");
                // Lost focus for an unbounded amount of time: stop playback and release media player
                if (BrandedApplication.getContext().isPlaying()) {
                    audioInterrupted = true;
                    releasePlayer();
                }
                break;
        }
    }

    @Override
    public void onDestroy() {
        System.out.println("SERVICE STOPPED");
        releasePlayer();
        mediaSession.release();
    }
}

And here's RemoteReceiver.java:

public class RemoteReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {
            final KeyEvent event = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);

            if (event != null && event.getAction() == KeyEvent.ACTION_DOWN) {
                switch (event.getKeyCode()) {
                    case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
                        context.startService(new Intent(context, StreamService.class));
                        break;
                }
            }
        }
    }
}
like image 691
codeman Avatar asked Nov 17 '15 15:11

codeman


Video Answer


2 Answers

Okay, from the additional information you provided, I believe I know what the issue is. In Android 5.0 Lock Screen Controls were removed. They are now implemented via the Notification API. So try adding the following to your notification builder.

notification.setStyle(new NotificationCompat.MediaStyle()
                .setShowActionsInCompactView(0)
                .setMediaSession(mediaSession));
            .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)

That should place it on your lock screen. I would also suggest changing the Notification.PRIORITY_DEFAULT as well as include an action to your notification otherwise you won't be able to control the playback.

like image 161
phxhawke Avatar answered Oct 08 '22 18:10

phxhawke


I know this post is late but if anyone is still facing the issue.This will show up in your lock screen also.

Here is the code for notification builder class-

      import android.annotation.SuppressLint;
        import android.app.NotificationChannel;
        import android.app.NotificationManager;
        import android.app.PendingIntent;
        import android.app.Service;
        import android.content.Context;
        import android.content.Intent;
        import android.graphics.BitmapFactory;
        import android.media.MediaPlayer;
        import android.media.session.MediaSessionManager;
        import android.os.Build;
        import android.os.IBinder;
        import android.os.RemoteException;
        import android.support.annotation.RequiresApi;
        import android.support.v4.app.NotificationCompat;
        import android.support.v4.media.session.MediaControllerCompat;
        import android.support.v4.media.session.MediaSessionCompat;
        import android.util.Log;



        import org.json.JSONException;


        public class MediaPlayerService extends Service {


            private static final String  CHANNEL_ID = "my_channel_01";

            public static final String ACTION_PLAY = "action_play";
            public static final String ACTION_PAUSE = "action_pause";
            public static final String ACTION_NEXT = "action_next";
            public static final String ACTION_PREVIOUS = "action_previous";
            public static final String ACTION_STOP = "action_stop";
            public static final String ACTION_NOTHING = "action_previous";


            private NotificationManager notificationManager;
            NotificationManager mNotificationManager;

            private MediaPlayer mMediaPlayer;
            private MediaSessionManager mManager;
            private MediaSessionCompat mSession;
            private MediaControllerCompat mController;



            private MediaPlayerService mService;

            String title = null; 
            String description = null; 

            @Override
            public IBinder onBind(Intent intent) {
                return null;
            }

            private void handleIntent(Intent intent) {
                if (intent == null || intent.getAction() == null)
                    return;

                String action = intent.getAction();

                if (action.equalsIgnoreCase(ACTION_PLAY)) {
                    mController.getTransportControls().play();
                } else if (action.equalsIgnoreCase(ACTION_PAUSE)) {
                    mController.getTransportControls().pause();
                } else if (action.equalsIgnoreCase(ACTION_PREVIOUS)) {
                    mController.getTransportControls().skipToPrevious();
                } else if (action.equalsIgnoreCase(ACTION_NEXT)) {
                    mController.getTransportControls().skipToNext();
                } else if (action.equalsIgnoreCase(ACTION_STOP)) {
                    mController.getTransportControls().stop();
                }


            }

            private NotificationCompat.Action generateAction(int icon, String title, String intentAction) {
                Intent intent = new Intent(getApplicationContext(), MediaPlayerService.class);
                intent.setAction(intentAction);
                PendingIntent pendingIntent = PendingIntent.getService(getApplicationContext(), 1, intent, 0);
                return new NotificationCompat.Action.Builder(icon, title, pendingIntent).build();

            }



            @SuppressLint("ServiceCast")
            private void buildNotification(NotificationCompat.Action action) {




                    title = ""; // add variable to get current playing song title here 


                    description =""; // add variable to get current playing song description  here


                Intent notificationIntent = new Intent(getApplicationContext(), HomeActivity.class); //specify which activity should be opened when widget is clicked (other than buttons)

                PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, 0);

                notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

                // Notification channels are only supported on Android O+.
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) 
                {
                    //method to create channel if android version is android. Descrition below
                    createNotificationChannel();
                }

                Intent intent = new Intent(getApplicationContext(), MediaPlayerService.class);
                intent.setAction(ACTION_STOP);
                PendingIntent pendingIntent = PendingIntent.getService(getApplicationContext(), 1, intent, 0);
                final NotificationCompat.Builder builder;

                //condition to check if music is playing
                //if music is playing widget cant be dismissed on swipe

                if(<add your method to check play status here>)
                {
                    builder = new NotificationCompat.Builder(this, CHANNEL_ID)
                            .setSmallIcon(R.drawable.logo2b)
                            .setLargeIcon(BitmapFactory.decodeResource(getApplication().getResources(), R.mipmap.ic_launcher))
                            .setContentTitle(title)
                            .setContentText(description)
                            .setDeleteIntent(pendingIntent)
                            .setContentIntent(contentIntent)
                            .setChannelId(CHANNEL_ID)
                            .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
                            .setOnlyAlertOnce(true)
                            .setColor(getResources().getColor(R.color.colorPrimary))
                            .setOngoing(true) //set this to true if music is playing widget cant be dismissed on swipe
                            .setStyle(new android.support.v4.media.app.NotificationCompat.MediaStyle()
                                    // show only play/pause in compact view
                                    .setShowActionsInCompactView(0, 1, 2));
                }
                //else if music is not playing widget can be dismissed on swipe
                else
                {
                    builder = new NotificationCompat.Builder(this, CHANNEL_ID)
                            .setSmallIcon(R.drawable.logo2b)
                            .setLargeIcon(BitmapFactory.decodeResource(getApplication().getResources(), R.mipmap.ic_launcher))
                            .setContentTitle(title)
                            .setContentText(description)
                            .setDeleteIntent(pendingIntent)
                            .setContentIntent(contentIntent)
                            .setChannelId(CHANNEL_ID)
                            .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
                            .setOnlyAlertOnce(true)
                            .setColor(getResources().getColor(R.color.colorPrimary))
                            .setStyle(new android.support.v4.media.app.NotificationCompat.MediaStyle()
                                    // show only play/pause in compact view
                                    .setShowActionsInCompactView(0, 1, 2));
                }



                builder.addAction(generateAction(R.drawable.ic_skip_previous_white_24dp, "Previous", ACTION_PREVIOUS));
                builder.addAction(action);
                builder.addAction(generateAction(R.drawable.ic_skip_next_white_24dp, "Next", ACTION_NEXT));
                //style.setShowActionsInCompactView(0,1,2);

                //   builder.setColor(getResources().getColor(R.color.app_orange_color));


                notificationManager.notify(1, builder.build());

            }

            @Override
            public int onStartCommand(Intent intent, int flags, int startId) {
                if (mManager == null) {
                    try {
                        initMediaSessions();
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }

                handleIntent(intent);
                return super.onStartCommand(intent, flags, startId);
            }

            private void initMediaSessions() throws RemoteException {
                mMediaPlayer = new MediaPlayer();
                mSession = new MediaSessionCompat(getApplicationContext(), "simple player session");
                mController = new MediaControllerCompat(getApplicationContext(), mSession.getSessionToken());



                mSession.setCallback(new MediaSessionCompat.Callback() {
                                         @Override
                                         public void onPlay() {
                                             super.onPlay();


                                             //add you code for play button click here

   //replace your drawable id that shows pauseicon                                              buildNotification(generateAction(R.drawable.uamp_ic_pause_white_24dp, "Pause", ACTION_PAUSE));




                                         }

                                         @Override
                                         public void onPause() {
                                             super.onPause();

                                              //add you code for pause button click here
         //replace your drawable id that shows play icon                                    buildNotification(generateAction(R.drawable.uamp_ic_play_arrow_white_24dp, "Play", ACTION_PLAY)); 

                                         }

                                         @Override
                                         public void onSkipToNext() {

                                             super.onSkipToNext();

                                             //add you code for next button click here
                                             buildNotification(generateAction(R.drawable.uamp_ic_pause_white_24dp, "Pause", ACTION_PAUSE));


                                         }

                                         @Override
                                         public void onSkipToPrevious() {
                                             super.onSkipToPrevious();

                                             //add you code for previous button click here

                                             buildNotification(generateAction(R.drawable.uamp_ic_pause_white_24dp, "Pause", ACTION_PAUSE));

                                         }

                                         @Override
                                         public void onStop() {
                                             super.onStop();
                                             Log.e("MediaPlayerService", "onStop");
                                             //Stop media player and dismiss widget here
                                             NotificationManager notificationManager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
                                             notificationManager.cancel(1);
                                             Intent intent = new Intent(getApplicationContext(), MediaPlayerService.class);
                                             stopService(intent);
                                         }

                                         @Override
                                         public void onSeekTo(long pos) {
                                             super.onSeekTo(pos);
                                         }


                                     }
                );
            }



            @Override
            public boolean onUnbind(Intent intent) {
                mSession.release();
                return super.onUnbind(intent);
            }



            //method to create notification channel on android Oreo and above
            @RequiresApi(Build.VERSION_CODES.O)
            private void createNotificationChannel() {
                int notifyID = 1;

                CharSequence name = "Player Widget";// The user-visible name of the channel. This channel name will be shown in settings.
                if (notificationManager.getNotificationChannel(CHANNEL_ID) == null) {
                    NotificationChannel notificationChannel =
                            new NotificationChannel(CHANNEL_ID, name, NotificationManager.IMPORTANCE_LOW);


                    notificationManager.createNotificationChannel(notificationChannel);
                }
            }

 }

And fire these intents for actions to update widget when play status is changed from within the app:

Play-

//to change widgets current action button to play
 Intent intent = new Intent(getApplicationContext(), MediaPlayerService.class);
                intent.setAction(MediaPlayerService.ACTION_PAUSE);
                startService(intent);

Pause-

 //to change widgets current action button to pause
        Intent intent = new Intent(getApplicationContext(), MediaPlayerService.class);
                        intent.setAction(MediaPlayerService.ACTION_PLAY);
                        startService(intent);

Excuse me if there are any unwanted import.All the best.

like image 26
Nabil Mohammed Nalakath Avatar answered Oct 08 '22 18:10

Nabil Mohammed Nalakath