C

Media Session Controller

Demonstrates media session API usage.

"View with one active media session"

Building and deploying the example

See specific steps relating to building and deploying Qt for Android Automotive examples.

Overview

This example demonstrates the usage of Qt Media Session API to control media sessions on Android Automotive.

Once a media session is started, the app shows a list containing all currently active sessions. Each of the items in the list represents one session, and can be manipulated via the controls provided.

Including the API

To use the media session API from QML, we first need to import the Qt for Android Automotive Media module:

import QtAndroidAutomotive.Media

Starting the listener service

In order to receive data about media sessions, the app is an enabled notificationlistener. This can be achieved by declaring the notificationlistener service provided by the media sessions API in our AndroidManifest.xml file:

        <service
            android:name="org.qtproject.qt.android.mediasession.QtMediaNotificationListener"
            android:enabled="true"
            android:exported="true"
            android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
            <intent-filter>
                <action android:name="android.service.notification.NotificationListenerService" />
            </intent-filter>
        </service>

Listing media session controllers

Controller objects are used for controlling sessions. These controllers can be acquired from the MediaSessionManager::activeControllers property:

    ListView {
        id: sessionsList

        spacing: 10
        model: MediaSessionManager.activeControllers
        clip: true

        anchors {
            left: parent.left
            top: parent.top
            right: parent.right
            bottom: parent.bottom
            margins: 10
        }

        delegate: MediaControllerItem {
            controller: modelData
        }
    }

In this list, each controller is represented by a MediaControllerItem QML element.

Using the controller

Media title and artist

The example uses the QMediaMetaData returned by MediaSessionController::metaData property:

Text {
    id: titleText

    readonly property int titleKey: 0

    width: parent.width
    elide: Text.ElideRight
    color: "#f3f3f4"
    text: root.controller.metaData.stringValue(titleKey)
}

Text {
    id: artistText

    readonly property int artistKey: 19

    color: "#cecfd5"
    text: root.controller.metaData.stringValue(artistKey)
}
Controlling the media session

In order to control the media, we have some buttons that use the MediaSessionController::pause, MediaSessionController::play, MediaSessionController::skipToNext, and MediaSessionController::skipToPrevious methods.

Whether or not these controls are available is defined separately by each session. Availability of control functionalities can be detected by using the MediaSessionController::availableActions property.

MediaControlButton {
    id: prevButton

    onClicked: root.controller.skipToPrevious()

    visible: root.controller.availableActions &
             MediaSessionController.SkipToPreviousAction
    width: buttonRow.buttonSize
    height: buttonRow.buttonSize
    icon.source: "res/previous.png"
}

MediaControlButton {
    id: playButton

    onClicked: {
        if (playing)
            root.controller.pause()
        else
            root.controller.play()
    }

    readonly property bool playing: root.controller.playbackState ===
                                    MediaSessionController.PlayingState
    visible: root.controller.availableActions &
             (playing ?
                  MediaSessionController.PauseAction :
                  MediaSessionController.PlayAction)
    width: buttonRow.buttonSize
    height: buttonRow.buttonSize
    icon.source: {
        if (root.controller.playbackState ===
                MediaSessionController.PlayingState)
            return "res/pause.png"
        else
            return "res/play.png"
    }
}

MediaControlButton {
    id: nextButton

    onClicked: root.controller.skipToNext()

    visible: root.controller.availableActions &
             MediaSessionController.SkipToNextAction
    width: buttonRow.buttonSize
    height: buttonRow.buttonSize
    icon.source: "res/next.png"
}

MediaControlButton {
    id: stopButton

    onClicked: root.controller.stop()

    visible: root.controller.availableActions &
             MediaSessionController.StopAction
    width: buttonRow.buttonSize
    height: buttonRow.buttonSize
    icon.source: "res/stop.png"
}
Seeking to a position inside the media

For seeking through the media, the example provides a Slider that uses the MediaSessionController::position property to read and control the current playback position and the MediaSessionController::duration property to read the length of the media:

Slider {
    id: progressSlider

    onMoved: {
        // Position is set in milliseconds:
        root.controller.position = value * 1000
    }

    width: parent.width
    from: 0
    // Duration and position are reported in milliseconds, converting to seconds
    to: (root.controller.duration / 1000)
    value: (root.controller.position / 1000)
    enabled: root.controller.availableActions & MediaSessionController.PositionAction
}

To complete the display of playback duration and position, the example has two text elements. One to display the current position, the other to show the length of the media. These are read directly from the slider implemented above and formatted to be displayed in minutes and seconds:

Text {
    id: progressText

    readonly property string minutes:
        "%1".arg(parseInt(progressSlider.value / 60)).padStart(2, '0')

    readonly property string seconds:
        "%1".arg(parseInt(progressSlider.value % 60)).padStart(2, '0')

    color: "#cecfd5"
    text: "%1:%2".arg(minutes).arg(seconds)
    verticalAlignment: Text.AlignVCenter

    anchors {
        left: parent.left
        leftMargin: rootColumn.spacing
        verticalCenter: parent.verticalCenter
    }
}
Text {
    id: durationText

    readonly property string minutes:
        "%1".arg(parseInt(progressSlider.to / 60)).padStart(2, '0')
    readonly property string seconds:
        "%1".arg(parseInt(progressSlider.to % 60)).padStart(2, '0')

    color: "#cecfd5"
    text: "%1:%2".arg(minutes).arg(seconds)
    verticalAlignment: Text.AlignVCenter

    anchors {
        right: parent.right
        rightMargin: rootColumn.spacing
        verticalCenter: parent.verticalCenter
    }
}

Available under certain Qt licenses.
Find out more.