Music Player Control in Notification

how can i show media controls for my music player website in the notification tray using javascript/Jquery?

I think the API you are looking for is the Media Session API. If you register the handlers for the appropriate actions the browser should display buttons which trigger those handlers.

navigator.mediaSession.setActionHandler('previoustrack', () => {
// Whatever you need to do to play the previous track.
});
navigator.mediaSession.setActionHandler('nexttrack', () => {
// Whatever you need to do to play the next track.
});

Music player control in notification

You need to set a custom intent action, not the AudioPlayerBroadcastReceiver component class.

Create a Intent with custom action name like this

  Intent switchIntent = new Intent("com.example.app.ACTION_PLAY");

Then, register the PendingIntent Broadcast receiver

  PendingIntent pendingSwitchIntent = PendingIntent.getBroadcast(this, 100, switchIntent, 0);

Then, set a onClick for the play control , do similar custom action for other controls if required.

  notificationView.setOnClickPendingIntent(R.id.btn_play_pause_in_notification, pendingSwitchIntent);

Next,Register the custom action in AudioPlayerBroadcastReceiver like this

   <receiver android:name="com.example.app.AudioPlayerBroadcastReceiver" >
<intent-filter>
<action android:name="com.example.app.ACTION_PLAY" />
</intent-filter>
</receiver>

Finally, when play is clicked on Notification RemoteViews layout, you will receive the play action by the BroadcastReceiver

public class AudioPlayerBroadcastReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {

String action = intent.getAction();

if(action.equalsIgnoreCase("com.example.app.ACTION_PLAY")){
// do your stuff to play action;
}
}
}

EDIT: how to set the intent filter for Broadcast receiver registered in code

You can also set the Custom Action through Intent filter from code for the registered Broadcast receiver like this

    // instance of custom broadcast receiver
CustomReceiver broadcastReceiver = new CustomReceiver();

IntentFilter intentFilter = new IntentFilter();
intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
// set the custom action
intentFilter.addAction("com.example.app.ACTION_PLAY");
// register the receiver
registerReceiver(broadcastReceiver, intentFilter);

Music Controls in Notification

Your notification actions gets received by Notifications service, not the Music service. Edit notification intents to target Music Service

Intent playIntent = new Intent(this, MusicService.class);
//... edit all of them

Move onStartCommand() method to Music Service and assign actions

@Override
public int onStartCommand(Intent intent, int flags, int startId) {

if (intent.getAction().equals(Constant.ACTION.PLAY_ACTION)) {
//call play method
}
//...etc

except STARTFOREGROUND_ACTION and STOPFOREGROUND_ACTION, leave it in Notification Service's onStartCommand(). Also insert null check on Intent in both services.

How do I make a media player control notification in Gtk?

I think the technology used by Rhytmbox to acheive this is MPRIS (Media Player Remote Interfacing Specification). As they say on their we page:

The Media Player Remote Interfacing Specification (MPRIS) is a
standard D-Bus interface which aims to provide a common programmatic
API for controlling media players.

It provides a mechanism for discovery, querying and basic playback
control of compliant media players, as well as a tracklist interface
which is used to add context to the active media item.

In Rhythmbox, this is implemented as a core plug-in. The code is pretty complicated, but basically implements the MPRIS specification. This implementation then exposes some information and controls out to other applications which want to control the Rhythmbox, like the Gnome Shell in your case. If you deactivate the MPRIS plug-in, the "notification player" will no longer work.

In GNOME Shell, you can see they have their own MPRIS module as well (https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/mpris.js), which is used to:

  1. Respond to player notifications and display information (album art, title, buttons, etc).
  2. Send notifications to the player (ex.: pause the song).

In this module, the formatting of the buttons and all that stuff comes into life as well. This means that on your part (the player's designer), you should have no UI formatting to do (you don't have control over this, GNOME Shell has). What you need to do is expose what is needed by the GNOME Shell by implementing the MPRIS interfaces.

(As a side note: the calendar.js file is the one implementing the notification list, and you can see it uses MPRIS "notifications", which it puts on top of standard notifications.)

There exist Python libraries to do so, such as Mopidy-MPRIS, bit their support for the GNOME Shell seems not to be working at the moment. You may have to look for D-Bus related libraries on FreeDesktop.org. There exists many bindings, with some in Python.

I hope this points you in the right direction.

Java Android Media Player (Notification)

You should follow this old google mediaplayer sample (Use this commit if the master version crashes for you) if you want to keep using the MediaPlayer.

But If you don't mind using ExoPlayer, then you should instead follow this new one, since ExoPlayer can already take care of part of the notification stuff.

But so in these sample projects you can see that:

  1. It's not as simple as just a notification, it also ties into people pressing on a play button on their headset etc.
  2. You really need a service for this, to bind to the notification, to handle the action.MEDIA_BUTTON intents.

Minimal code for adding such a notification with a MediaSession and Service with the MediaPlayer (so not ExoPlayer) like you are using would look something like this:
Add the media compat libraries to your apps' build gradle:

dependencies {
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation "androidx.media:media:1.1.0"
}

Then create a class for creating the Notification:

package com.radiomedia.a1liferadio;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.graphics.Color;
import android.os.Build;
import android.support.v4.media.MediaDescriptionCompat;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;
import androidx.core.content.ContextCompat;
import androidx.media.app.NotificationCompat.MediaStyle;
import androidx.media.session.MediaButtonReceiver;

/**
* Keeps track of a notification and updates it automatically for a given MediaSession. This is
* required so that the music service don't get killed during playback.
*/
public class MediaNotificationManager {

public static final int NOTIFICATION_ID = 412;

private static final String TAG = MediaNotificationManager.class.getSimpleName();
private static final String CHANNEL_ID = "com.example.android.musicplayer.channel";
private static final int REQUEST_CODE = 501;

private final MediaSessionService mService;

private final NotificationCompat.Action mPlayAction;
private final NotificationCompat.Action mPauseAction;
private final NotificationManager mNotificationManager;

public MediaNotificationManager(MediaSessionService musicContext) {
mService = musicContext;

mNotificationManager =
(NotificationManager) mService.getSystemService(Service.NOTIFICATION_SERVICE);

mPlayAction =
new NotificationCompat.Action(
R.drawable.ic_play,
"play",
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService,
PlaybackStateCompat.ACTION_PLAY));
mPauseAction =
new NotificationCompat.Action(
R.drawable.ic_pause,
"pause",
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService,
PlaybackStateCompat.ACTION_PAUSE));
// Cancel all notifications to handle the case where the Service was killed and
// restarted by the system.
mNotificationManager.cancelAll();
}

public void onDestroy() {
Log.d(TAG, "onDestroy: ");
}

public NotificationManager getNotificationManager() {
return mNotificationManager;
}

public Notification getNotification(MediaMetadataCompat metadata,
@NonNull PlaybackStateCompat state,
MediaSessionCompat.Token token) {
boolean isPlaying = state.getState() == PlaybackStateCompat.STATE_PLAYING;
MediaDescriptionCompat description = metadata.getDescription();
NotificationCompat.Builder builder =
buildNotification(state, token, isPlaying, description);
return builder.build();
}

private NotificationCompat.Builder buildNotification(@NonNull PlaybackStateCompat state,
MediaSessionCompat.Token token,
boolean isPlaying,
MediaDescriptionCompat description) {

// Create the (mandatory) notification channel when running on Android Oreo.
if (isAndroidOOrHigher()) {
createChannel();
}

NotificationCompat.Builder builder = new NotificationCompat.Builder(mService, CHANNEL_ID);
builder.setStyle(
new MediaStyle()
.setMediaSession(token)
.setShowActionsInCompactView(0)
// For backwards compatibility with Android L and earlier.
.setShowCancelButton(true)
.setCancelButtonIntent(
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService,
PlaybackStateCompat.ACTION_STOP)))
.setColor(ContextCompat.getColor(mService, R.color.colorPrimary))
.setSmallIcon(R.drawable.ic_play)
// Pending intent that is fired when user clicks on notification.
.setContentIntent(createContentIntent())
// Title - Usually Song name.
.setContentTitle(description.getTitle())
// When notification is deleted (when playback is paused and notification can be
// deleted) fire MediaButtonPendingIntent with ACTION_PAUSE.
.setDeleteIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(
mService, PlaybackStateCompat.ACTION_PAUSE));

builder.addAction(isPlaying ? mPauseAction : mPlayAction);

return builder;
}

// Does nothing on versions of Android earlier than O.
@RequiresApi(Build.VERSION_CODES.O)
private void createChannel() {
if (mNotificationManager.getNotificationChannel(CHANNEL_ID) == null) {
// The user-visible name of the channel.
CharSequence name = "MediaSession";
// The user-visible description of the channel.
String description = "MediaSession and MediaPlayer";
int importance = NotificationManager.IMPORTANCE_LOW;
NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, name, importance);
// Configure the notification channel.
mChannel.setDescription(description);
mChannel.enableLights(true);
// Sets the notification light color for notifications posted to this
// channel, if the device supports this feature.
mChannel.setLightColor(Color.RED);
mChannel.enableVibration(true);
mChannel.setVibrationPattern(
new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
mNotificationManager.createNotificationChannel(mChannel);
Log.d(TAG, "createChannel: New channel created");
} else {
Log.d(TAG, "createChannel: Existing channel reused");
}
}

private boolean isAndroidOOrHigher() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
}

private PendingIntent createContentIntent() {
Intent openUI = new Intent(mService, MainActivity.class);
openUI.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
return PendingIntent.getActivity(
mService, REQUEST_CODE, openUI, PendingIntent.FLAG_CANCEL_CURRENT);
}

}

Then Create a Service class that handles playback and the notification button presses:

package com.radiomedia.a1liferadio;

import android.app.Notification;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.IBinder;
import android.os.SystemClock;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.view.KeyEvent;

import androidx.annotation.Nullable;

public class MediaSessionService extends Service {
public MediaPlayer mediaPlayer;
public static final String TAG = "MediaSessionService";
public static final int NOTIFICATION_ID = 888;
private MediaNotificationManager mMediaNotificationManager;
private MediaSessionCompat mediaSession;

@Override
public void onCreate() {
super.onCreate();
mediaPlayer = new MediaPlayer();
mMediaNotificationManager = new MediaNotificationManager(this);
mediaSession = new MediaSessionCompat(this, "SOME_TAG");
mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
mediaSession.setCallback(new MediaSessionCompat.Callback() {
@Override
public void onPlay() {
mediaPlayer.start();
}

@Override
public void onPause() {
mediaPlayer.pause();
}
});
Notification notification =
mMediaNotificationManager.getNotification(
getMetadata(), getState(), mediaSession.getSessionToken());

startForeground(NOTIFICATION_ID, notification);
}

public MediaMetadataCompat getMetadata() {
MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder();

builder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, "artist");
builder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, "title");
builder.putLong(
MediaMetadataCompat.METADATA_KEY_DURATION, mediaPlayer.getDuration()
);
return builder.build();
}

private PlaybackStateCompat getState() {
long actions = mediaPlayer.isPlaying() ? PlaybackStateCompat.ACTION_PAUSE : PlaybackStateCompat.ACTION_PLAY;
int state = mediaPlayer.isPlaying() ? PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_PAUSED;

final PlaybackStateCompat.Builder stateBuilder = new PlaybackStateCompat.Builder();
stateBuilder.setActions(actions);
stateBuilder.setState(state,
mediaPlayer.getCurrentPosition(),
1.0f,
SystemClock.elapsedRealtime());
return stateBuilder.build();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if ("android.intent.action.MEDIA_BUTTON".equals(intent.getAction())) {
KeyEvent keyEvent = (KeyEvent) intent.getExtras().get("android.intent.extra.KEY_EVENT");
if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PAUSE) {
mediaPlayer.pause();
} else {
mediaPlayer.start();
}
}
return super.onStartCommand(intent, flags, startId);
}

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

}

Update your manifest for declaring the service and sending the action.MEDIA_BUTTON intent to it.

<service
android:name=".MediaSessionService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON"/>
</intent-filter>
</service>
<receiver android:name="androidx.media.session.MediaButtonReceiver">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON"/>
</intent-filter>
</receiver>

This MediaButtonReceiver above is needed for pre-OREO devices, it will forward the event to the service on those platforms.

Then start the service from your main activity.

ContextCompat.startForegroundService(
MainActivity.this.getApplicationContext(),
new Intent(MainActivity.this.getApplicationContext(), MediaSessionService.class));

Things still to do here are making your main activity send actions to your service or the player in the service somehow, updating the notification and service state on a change, but you should be able to get something working with this code and figure out the rest from the google sample code.

How to put media controller button on notification bar?

For getting media player controller in your app simply follow this:

Call this method in your MainActivity

public void showNotification(View view){
new MyNotification(this);
finish();
}

Create a new MynotificationClass

public class MyNotification extends Notification {

private Context ctx;
private NotificationManager mNotificationManager;

@SuppressLint("NewApi")
public MyNotification(Context ctx){
super();
this.ctx=ctx;
String ns = Context.NOTIFICATION_SERVICE;
mNotificationManager = (NotificationManager) ctx.getSystemService(ns);
CharSequence tickerText = "Shortcuts";
long when = System.currentTimeMillis();
Notification.Builder builder = new Notification.Builder(ctx);
@SuppressWarnings("deprecation")
Notification notification=builder.getNotification();
notification.when=when;
notification.tickerText=tickerText;
notification.icon=R.drawable.ic_launcher;

RemoteViews contentView=new RemoteViews(ctx.getPackageName(), R.layout.messageview);

//set the button listeners
setListeners(contentView);

notification.contentView = contentView;
notification.flags |= Notification.FLAG_ONGOING_EVENT;
CharSequence contentTitle = "From Shortcuts";
mNotificationManager.notify(548853, notification);
}

public void setListeners(RemoteViews view){
//radio listener
Intent radio=new Intent(ctx,HelperActivity.class);
radio.putExtra("DO", "radio");
PendingIntent pRadio = PendingIntent.getActivity(ctx, 0, radio, 0);
view.setOnClickPendingIntent(R.id.radio, pRadio);

//volume listener
Intent volume=new Intent(ctx, HelperActivity.class);
volume.putExtra("DO", "volume");
PendingIntent pVolume = PendingIntent.getActivity(ctx, 1, volume, 0);
view.setOnClickPendingIntent(R.id.volume, pVolume);

//reboot listener
Intent reboot=new Intent(ctx, HelperActivity.class);
reboot.putExtra("DO", "reboot");
PendingIntent pReboot = PendingIntent.getActivity(ctx, 5, reboot, 0);
view.setOnClickPendingIntent(R.id.reboot, pReboot);

//top listener
Intent top=new Intent(ctx, HelperActivity.class);
top.putExtra("DO", "top");
PendingIntent pTop = PendingIntent.getActivity(ctx, 3, top, 0);
view.setOnClickPendingIntent(R.id.top, pTop);*/

//app listener
Intent app=new Intent(ctx, com.example.demo.HelperActivity.class);
app.putExtra("DO", "app");
PendingIntent pApp = PendingIntent.getActivity(ctx, 4, app, 0);
view.setOnClickPendingIntent(R.id.btn1, pApp);
}

}

Create a HelperActivity class

public class HelperActivity extends Activity {

private HelperActivity ctx;

@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
ctx = this;
String action = (String) getIntent().getExtras().get("DO");
if (action.equals("radio")) {
//Your code
} else if (action.equals("volume")) {
//Your code
} else if (action.equals("reboot")) {
//Your code
} else if (action.equals("top")) {
//Your code
} else if (action.equals("app")) {
//Your code
}

if (!action.equals("reboot"))
finish();
}

@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
}
}

XML layout for Notificationlayout.xml

<?xml version="1.0" encoding="UTF-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >

<TextView
android:id="@+id/msglbl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="test" />

<TextView
android:id="@+id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/msglbl" />

<Button
android:id="@+id/btn1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="play" android:layout_margin="10dp"/>

</RelativeLayout>


Related Topics



Leave a reply



Submit