Accessing media notification areas

The MediaPlayer class provides the functionality to play media content. However, the NowPlayingConnection class notifies the system and your app of external events, such as skip, play and pause, or revoked. The NowPlayingConnection class also gives your app system access to the volume overlay and the media notification area. The volume overlay is where the media that is currently playing on the device is described to your user.

Screen showing the volume overlay.

Volume overlay and media notification area

The media notification area is visible in the volume overlay when the overlayStyle property is set to OverlayStyle::Fancy. The media notification area is where you'll find information on media metadata and also any artwork that represents the media content that's currently playing. The media notification area is where you'll also find playback controls.

Connection priority

A NowPlayingConnection can only serve one user of its connection service at a time. The NowPlayingConnection uses two connection priorities to manage its service availability. The connection priorities are either low (which is preemptible) or high (which is non-preemptible). Preemptible connections can be automatically restarted, as long as their audio routing has not changed to a more public device, such as from a headset to the handset. Non-preemptible connections cannot be restarted, and can be revoked by a connection with a higher priority.

Before the connection service can be used to record or play any media content, it must be acquired by a connection service user, such as the media player. When a connection is acquired, it's considered active. If the connection service is already playing a media file, the service may revoke any previously connected users. This process of revocation is not automatic, it's decided by connection priority, which controls whether the connection is considered by the system to be preemptible or non-preemptible. Connection revocation is done to give exclusive use of the connection service to the acquiring connection user, which becomes the current user of the NowPlayingConnection.

A connection is considered to be preemptible if it can be automatically paused and subsequently restarted by the connection service. Preemptible connections remain active while they're paused, which allows them to be automatically restarted by the service.

There are several use cases that help demonstrate connection priority.

  • Low priority connection acquired by another low priority connection: If a low priority connection is currently acquired and another low priority connection tries to acquire the service, the first connection is revoked. When a connection has been revoked, it's no longer considered to be acquired, and cannot be automatically restarted. In this use case, the first low priority connection is considered to be non-preemptible because it cannot be automatically restarted.
  • Low priority connection acquired by a high priority connection: If a low priority connection is currently acquired and a high priority connection tries to acquire the service, the first connection is preempted. This means that the first connection's use of the service is temporarily paused (made non-active), but the connection still remains acquired. When the second connection (with higher priority) has finished using the service and is revoked, then the first connection automatically becomes active again. In this use case, the low priority connection is considered to be preemptible, which means that it can be automatically paused and subsequently restarted.
  • High priority connection acquired by another low or high priority connection: If a high priority connection is currently acquired, any attempt by any other connection, either with low or high priority, to acquire the service will fail with a "resource unavailable" error. In this use case, all subsequent connections are considered to be non-preemptible because they're all automatically revoked as they attempt to acquire the use of the connection service.

Notification area signals

The NowPlayingConnection class gives you access to the volume overlay. The volume overlay can be used to display information about the media that's currently playing.

The notification area emits signals that can be used by your app to keep track of when the Play, Pause, Prev or Next media player control buttons have been pressed.

The volume overlay on top of a stock media player app

Working with the notification area

Playing media that is located in the device's shared data areas requires file system access, access permissions, and some familiarity with the device file system. For more information about the shared data areas of the device, see Working with the file system.

Your app must acquire the necessary resources before it can attempt to play any media content. When your app has acquired the resources needed to play media content, it can begin to play the content and use the volume overlay to display media information to users. Your app can acquire exclusive access to the notification area, as you'll see in the following C++ code samples.

A QML sample

The acquired() signal is emitted when an app has successfully acquired exclusive access to the notification area. Using an onAcquired signal handler from within your QML code, your app can handle the acquired() signal by sending media data to the NowPlayingConnection object. The data is then used to set some important properties, such as duration and position. The following QML code sample demonstrates how to use the NowPlayingConnection object to set some media properties, and move the NowPlayingConnection object into the Started state. These NowPlayingConnection properties can only be set after the acquired() signal has been successfully received.

// A QML sample demonstrating how to acquire
// exclusive access to the notification area.

import bb.multimedia 1.2

// ...

attachedObjects: [
    NowPlayingConnection {
        id: nowPlaying

        connectionName: "myConnection"
        icon: "/tmp/artwork.png"

        onAcquired: {
            var metadata = { "track": "MyTrack", "artist" : "MyArtist" };

            // Set a duration of 2 minutes in milliseconds.
            nowPlaying.duration = 120000;
            nowPlaying.position = 0;

            // You can go from an Unprepared state directly to
            // the Started state. The Prepared state is implied.
            nowPlaying.mediaState = MediaState.Started;

            nowPlaying.setMetaData(metadata);
        }

        // A Pause command has been received from the UI.
        onPause: {
            mymedia.pause();
        }


        // A Play command has been received from the UI.
        onPlay: {
            mymedia.play();
        }

        // A Stop command has been received from the UI.
        onRevoked: {
            mymedia.stop();
        }
    }
]

// ...

onMyMediaWantsToPlay: {
    nowPlaying.acquire();
}

onMyMediaPaused: {
    nowPlaying.mediaState = NowPlayingConnection.MediaStatePaused;
}

onMyMediaPositionChanged: {
    nowPlaying.position = ms;
}

onMyMediaNoLongerWantsToPlay: {
  nowPlaying.revoke();
}

onMyMediaTrackHasChanged: {
  nowPlaying.trackChange();
}

// ...

While it's true that typically the acquired() signal is used to set media data, artwork, and initial position, it can also be used to make function calls to next(), previous(), play(), pause() or stop(). When the acquired() signal has been received, your app can call the most appropriate function to work with your media type.

You have the flexibility to set or update any of the properties of the NowPlayingConnection object until the revoked() signal is received. No other updates are possible after the revoked() signal has been received by the NowPlayingConnection object. It's important to note that signals that are triggered by the notification areas are only emitted while the service is acquired and not currently preempted by a connection with a higher priority, such as a phone connection.

A C++ code sample

The first step is to define a NowPlayingConnection object. In the following C++ code sample, the NowPlayingConnection object is named np. This object is used to acquire exclusive access to the notification area, and set some of the properties of the NowPlayingConnection. The following C++ code sample shows you how to create a pointer to a NowPlayingConnection object. In this code sample, the NowPlayingConnection() constructor, which takes two parameters, is used to create the instance of the NowPlayingConnection object. The first parameter is the name of the service that you want to connect to, and the second parameter is the parent object. In this case, the parent is the current object.

#include <bb/multimedia/NowPlayingConnection>

// ...

// Define a NowPlayingConnection object.
NowPlayingConnection np("myConnection", this);

// ...

The notification area is used for displaying media metadata in the volume overlay for content that is currently playing. It's also responsible for receiving signal notifications when the media playback controls displayed in it are used.

The next step is to define a QVariantMap to store some metadata that describes your media content. This metadata is used with your np object and the volume overlay to display information to your users. The following C++ code sample shows you how to define some metadata for your NowPlayingConnection.

// ...

// Define a QVariantMap object and
// set some metadata properties.
QVariantMap metadata;

metadata[MetaData::Title] = "Sample Track";
metadata[MetaData::Artist] = "Sample Artist";
metadata[MetaData::Album] = "Sample Album";

np->setMetaData(metadata);

// ...

Next, you can define the path to your artwork icon. This step is not necessary to use the NowPlayingConnection but it has been included here to show you how to do it. This icon can be any piece of artwork that represents your media content, such as CD cover art. The following C++ code sample shows you how to use the setIconUrl() function to set the path to your content artwork. The path is defined in the QUrl parameter.

// ...

// Set the path to the artwork icon
// for your media content.
np->setIconUrl(QUrl("file://%1/app/native/assets/artwork.png")
    .arg(QDir::currentPath()));

// ...

Finally, your app must make a call to the acquire() function to acquire exclusive access to the notification area. If the NowPlayingConnection is able to acquire exclusive access to the notification area, then it can continue to set some media content properties such as the duration of the track, and the initial starting position. It can also move the NowPlayingConnection object into the Started state.

// ...

// Acquire exclusive access to the notification
// area which is used for displaying now
// playing media metadata. 
np->acquire();

// If exclusive access to the notification area
// has been successfully acquired, we can set
// some properties of NowPlayingConnection.

// Represents 2 minute duration in milliseconds.
np->setDuration(120000);

// Represents the starting position of the track.
np->setPosition(0);

// Sets the media state of the NowPlayingConnection.
np->setMediaState(MediaState::Started);

// ...

Here are all of the above pieces of the code sample put together.

// A C++ sample demonstrating how to acquire
// exclusive access to the notification area.

#include <bb/multimedia/NowPlayingConnection>

// Define a NowPlayingConnection object.
NowPlayingConnection np(this);

// Define a QVariantMap object and
// set some metadata properties.
QVariantMap metadata;

metadata[MetaData::Title] = "Sample Track";
metadata[MetaData::Artist] = "Sample Artist";
metadata[MetaData::Album] = "Sample Album";

np->setMetaData(metadata);

// Set the path to the artwork icon
// for your media content.
np->setIconUrl(QUrl("file://%1/app/native/assets/artwork.png")
        .arg(QDir::currentPath()));

// Acquire exclusive access to the notification
// area which is used for displaying now
// playing media metadata. 
np->acquire();

// If exclusive access to the notification area
// has been successfully acquired, we can set
// some properties of NowPlayingConnection.

// Represents 2 minute duration in milliseconds.
np->setDuration(120000);

// Represents the starting position of the track.
np->setPosition(0);

// Sets the media state of the NowPlayingConnection.
np->setMediaState(MediaState::Started);

The controllerActive property

You can use the controllerActive property to determine if one or more controllers are actively listening for updates on the connection that is currently playing. You should update this property when your app has been started, even if your app remains inactive. You can use the controllerActive property to determine many useful things (for example, if the volume overlay is being displayed, if Bluetooth is connected, or if any other controller that you're looking for is currently active). You can use the isControllerActive() function to check the value of the controllerActive property. The isControllerActive() function returns true if one or more controllers are active on the connection that is currently playing, and false otherwise.

Last modified: 2013-12-20

comments powered by Disqus