Search code examples
javascriptandroidgoogle-chromeandroid-notificationsprogressive-web-apps

Media Notifications using the Media Session Web API doesn't work with Web Audio API


I'm trying to implement custom notifications into my PWA for the currently playing audio content.

As the title states; I'm using Android v8.1.0 with the Google Chrome App v68.0.x. According to this article: The Media Session API is supported in Chrome 57. I assume the version I'm using should be more than up-to-date to work with these notifications.

First, a code snipped for the audio content I play:

let context = getContext();
await context.resume().catch(console.error);
let source = context.createBufferSource();
source.connect(context.destination);
source.start(0);

The playing of audio content works just fine, no issue here. I tried updating the metadata of the Media Session API after the source.start(..) call, like this:

let updateMediaSession = (payload) => {
  if (!('mediaSession' in navigator)) {
    return;
  }

  navigator.mediaSession.metadata = new MediaMetadata({
    title: payload.title,
    artist: payload.artist,
    album: payload.album,
    artwork: [
      {
        src: '/static/logos/android-chrome-36x36.png',
        sizes: '36x36',
        type: 'image/png',
      },
      {
        src: '/static/logos/android-chrome-48x48.png',
        sizes: '48x48',
        type: 'image/png',
      },
      {
        src: '/static/logos/android-chrome-72x72.png',
        sizes: '72x72',
        type: 'image/png',
      },
      {
        src: '/static/logos/android-chrome-96x96.png',
        sizes: '96x96',
        type: 'image/png',
      },
      {
        src: '/static/logos/android-chrome-144x144.png',
        sizes: '144x144',
        type: 'image/png',
      },
      {
        src: '/static/logos/android-chrome-192x192.png',
        sizes: '192x192',
        type: 'image/png',
      },
      {
        src: '/static/logos/android-chrome-256x256.png',
        sizes: '256x256',
        type: 'image/png',
      },
      {
        src: '/static/logos/android-chrome-384x384.png',
        sizes: '384x384',
        type: 'image/png',
      },
      {
        src: '/static/logos/android-chrome-512x512.png',
        sizes: '512x512',
        type: 'image/png',
      },],
  });

  navigator.mediaSession.setActionHandler('play', () => ...);
  navigator.mediaSession.setActionHandler('pause', () => ...);
  navigator.mediaSession.setActionHandler('seekbackward', () => ...);
  navigator.mediaSession.setActionHandler('seekforward', () => ...);
  navigator.mediaSession.setActionHandler('previoustrack',() => ...);
  navigator.mediaSession.setActionHandler('nexttrack', () => ...);
};

I event update the playing state like so:

  if ('mediaSession' in navigator) {
    navigator.mediaSession.playbackState = 'paused';
  }

and

  if ('mediaSession' in navigator) {
    navigator.mediaSession.playbackState = 'playing';
  }

The Problem The notification doesn't show up on my smartphone.

I've deployed my source code to a proper server with domain. Navigated to the site on my smartphone, cleared all the data, uninstalled any potential PWAs and refreshed the page before conducting my tests.

It seems as if everything gets executed like one would expect but nothing is being displayed. I expected something like this.

Does anyone know why this doesn't work? enter image description here


Solution

  • You have two issues at play here. The first one is a really simple fix.

    updateNotification is preventing updateMediaSession from running, and would cause a double notification otherwise. You can fix this by simply wrapping updateNotification with a condition checking if(!('mediaSession' in navigation)). So if the device supports mediaSession, use mediaSession, otherwise (it's a computer) create a Notification.

    Second (and most important), using mediaSession requires an audio (or video) element to play for the notification to show up. It appears you're not using that.

    You can either change to using the audio element in the background and just rework your controls to modify that on interaction, or you can work around it by playing a silent audio clip in an invisible audio element and immediately pausing it (the notification will dismiss if it plays through). I would recommend using audio.

    If you decide to use the workaround, I found this repository with silent audio clips of varying lengths. The shortest one I could get to work was 5 seconds, any shorter would not show a notification for me.