C
Media Session Controller
Demonstrates media session API usage.
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
We need to import two Android Automotive modules: Media to use the media session API, and Base to use AndroidAppsUtils API to check for notifications access.
import QtAndroidAutomotive.Media import QtAndroidAutomotive.Base
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>
By default, apps do not have access to notifications. Therefore, if notifications are not accessible, we need to guide the user to the Settings app so that they can grant access to them.
if (!hasNotificationAccess()) { notificationsDialog.open() }
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 {} }
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 width: parent.width elide: Text.ElideRight color: "#f3f3f4" text: root.modelData.metaData.stringValue(MediaMetaData.Title) } Text { id: artistText color: "#cecfd5" text: root.modelData.metaData.stringValue(MediaMetaData.AlbumArtist) }
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.modelData.skipToPrevious() visible: root.modelData.availableActions & MediaSessionController.SkipToPreviousAction width: buttonRow.buttonSize height: buttonRow.buttonSize icon.source: "res/previous.png" } MediaControlButton { id: playButton onClicked: { if (playing) root.modelData.pause() else root.modelData.play() } readonly property bool playing: root.modelData.playbackState === MediaSessionController.PlayingState visible: root.modelData.availableActions & (playing ? MediaSessionController.PauseAction : MediaSessionController.PlayAction) width: buttonRow.buttonSize height: buttonRow.buttonSize icon.source: { if (root.modelData.playbackState === MediaSessionController.PlayingState) { return "res/pause.png"; } else { return "res/play.png"; } } } MediaControlButton { id: nextButton onClicked: root.modelData.skipToNext() visible: root.modelData.availableActions & MediaSessionController.SkipToNextAction width: buttonRow.buttonSize height: buttonRow.buttonSize icon.source: "res/next.png" } MediaControlButton { id: stopButton onClicked: root.modelData.stop() visible: root.modelData.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.modelData.position = value * 1000 } width: parent.width from: 0 // Duration and position are reported in milliseconds, converting to seconds to: (root.modelData.duration / 1000) value: (root.modelData.position / 1000) enabled: root.modelData.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.