--- /dev/null
+/*
+ * Copyright (C) 2016 The Qt Company Ltd.
+ * Copyright (C) 2017 Toyota Motor Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import QtQuick 2.6
+import QtQuick.Layouts 1.1
+import QtQuick.Controls 2.0
+import QtMultimedia 5.6
+import AGL.Demo.Controls 1.0
+import MediaPlayer 1.0
+import 'api' as API
+
+ApplicationWindow {
+ id: root
+
+ property int sourceID: 0
+ property bool registered_media: false
+ property int connectionID
+ property int sourceIndex
+
+ API.LightMediaScanner {
+ id: binding
+ url: bindingAddress
+ }
+
+ API.BluetoothManager {
+ id: bluetooth
+ url: bindingAddress
+ }
+
+ MediaPlayer {
+ id: player
+ audioRole: MediaPlayer.MusicRole
+ autoLoad: true
+ playlist: playlist
+
+ function time2str(value) {
+ return Qt.formatTime(new Date(value), 'mm:ss')
+ }
+ }
+ function stopMediaplayer(){
+ console.log("stop mediaplayer from C++")
+ player.pause()
+ }
+
+ function slotReply(msg){
+ var jstr = JSON.stringify(msg)
+ console.log("mediaplayer: Json jstr:" + jstr)
+ var content = JSON.parse(jstr);
+ var verb = content.response.verb
+ var err = content.response.error
+ console.log("mediaplayer: verb: " + verb)
+ console.log("mediaplayer: content.response.error: " + err)
+ switch(verb)
+ {
+ case "connect":
+ console.log("mediaplayer: replied by connect")
+ if(err == 0){
+ connectionID = content.response.mainConnectionID
+ console.log("mediaplayer: mainConnectionID is " + connectionID)
+ }
+ break;
+ case "registerSource":
+ console.log("mediaplayer: replied by registerSource")
+ if(err == 0){
+ sourceID = content.response.sourceID
+ registered_media = true
+ }
+ }
+ }
+
+ function slotEvent(event,msg){
+ var jstr = JSON.stringify(msg)
+ var content = JSON.parse(jstr);
+ var eventName = content.event
+ console.log("mediaplayer: event qml: event" + eventName + " msg: " + jstr)
+ switch(eventName)
+ {
+ case "soundmanager\/asyncSetSourceState":
+ // This event doesn't come for now
+ console.log("mediaplayer: soundmanager\/asyncSetSourceState")
+ console.log("mediaplayer: my soundID:" + sourceID + "handle:" + content.data.handle + ",sourceID:" + content.data.sourceID + ",sourceState:" + content.data.sourceState)
+ if(sourceID == content.data.sourceID){
+ console.log("mediaplayer: call ackSetSourceState")
+ var arg = JSON.stringify({handle:content.data.handle, error:0})
+ smw.call("ackSetSourceState", arg)
+ switch(content.data.sourceState){
+ case "on":
+ player.play()
+ break;
+ case "off":
+ player.pause()
+ break;
+ case "paused":
+ player.pause()
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ case "soundmanager\/asyncConnect":
+ // In reality, device should be opened in this timing
+ if(connectionID == content.data.connectionID){
+ //player.open_device()
+ }
+ break;
+ case "soundmanager\/asyncDisconnect":
+ // In reality, device should be closed in this timing
+ if(connectionID == content.data.connectionID){
+ // player.close_device()
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ Timer {
+ id: timer
+ interval: 250
+ running: (bluetooth.av_connected && bluetooth.state == "playing")
+ repeat: true
+ onTriggered: {
+ bluetooth.position = bluetooth.position + 250
+ slider.value = bluetooth.position
+ }
+ }
+
+ Playlist {
+ id: playlist
+ playbackMode: random.checked ? Playlist.Random : loop.checked ? Playlist.Loop : Playlist.Sequential
+ }
+
+
+ ColumnLayout {
+ anchors.fill: parent
+ Item {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.preferredHeight: 1080
+ clip: true
+ Image {
+ id: albumart
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ height: sourceSize.height * width / sourceSize.width
+ fillMode: Image.PreserveAspectCrop
+ source: player.metaData.coverArtImage ? player.metaData.coverArtImage : ''
+ visible: bluetooth.av_connected == false
+ }
+
+ Item {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ height :307
+ Rectangle {
+ anchors.fill: parent
+ color: 'black'
+ opacity: 0.75
+ }
+
+ ColumnLayout {
+ anchors.fill: parent
+ anchors.margins: root.width * 0.02
+ Item {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Row {
+ spacing: 20
+ ToggleButton {
+ id: random
+ visible: bluetooth.connected == false
+ offImage: './images/AGL_MediaPlayer_Shuffle_Inactive.svg'
+ onImage: './images/AGL_MediaPlayer_Shuffle_Active.svg'
+ }
+ ToggleButton {
+ id: loop
+ visible: bluetooth.connected == false
+ offImage: './images/AGL_MediaPlayer_Loop_Inactive.svg'
+ onImage: './images/AGL_MediaPlayer_Loop_Active.svg'
+ }
+ }
+ ColumnLayout {
+ anchors.fill: parent
+ Label {
+ id: title
+ Layout.alignment: Layout.Center
+ text: bluetooth.av_connected ? bluetooth.title : (player.metaData.title ? player.metaData.title : '')
+ horizontalAlignment: Label.AlignHCenter
+ verticalAlignment: Label.AlignVCenter
+ }
+ Label {
+ Layout.alignment: Layout.Center
+ text: bluetooth.av_connected ? bluetooth.artist : (player.metaData.contributingArtist ? player.metaData.contributingArtist : '')
+ horizontalAlignment: Label.AlignHCenter
+ verticalAlignment: Label.AlignVCenter
+ font.pixelSize: title.font.pixelSize * 0.6
+ }
+ }
+ }
+ Slider {
+ id: slider
+ Layout.fillWidth: true
+ to: bluetooth.av_connected ? bluetooth.duration : player.duration
+ enabled: bluetooth.av_connected == false
+ value: bluetooth.av_connected ? bluetooth.position : player.position
+ function getPosition() {
+ if (bluetooth.av_connected) {
+ return player.time2str(bluetooth.position)
+ }
+ return player.time2str(player.position)
+ }
+ Label {
+ id: position
+ anchors.left: parent.left
+ anchors.bottom: parent.top
+ font.pixelSize: 32
+ text: slider.getPosition()
+ }
+ Label {
+ id: duration
+ anchors.right: parent.right
+ anchors.bottom: parent.top
+ font.pixelSize: 32
+ text: bluetooth.av_connected ? player.time2str(bluetooth.duration) : player.time2str(player.duration)
+ }
+ onPressedChanged: player.seek(value)
+ }
+ RowLayout {
+ Layout.fillHeight: true
+// Image {
+// source: './images/AGL_MediaPlayer_Playlist_Inactive.svg'
+// }
+// Image {
+// source: './images/AGL_MediaPlayer_CD_Inactive.svg'
+// }
+ Item { Layout.fillWidth: true }
+ ImageButton {
+ id: previous
+ offImage: './images/AGL_MediaPlayer_BackArrow.svg'
+ onClicked: {
+ if (bluetooth.av_connected) {
+ bluetooth.sendMediaCommand("Previous")
+ bluetooth.position = 0
+ } else {
+ playlist.previous()
+ }
+ }
+ }
+ ImageButton {
+ id: play
+ offImage: './images/AGL_MediaPlayer_Player_Play.svg'
+ onClicked: {
+ if (bluetooth.av_connected) {
+ bluetooth.sendMediaCommand("Play")
+ } else {
+ console.log("mediaplayer: registered_media is " + registered_media)
+ if(registered_media)
+ {
+ console.log("mediaplayer: call connect")
+ var JsonArg = JSON.stringify({sourceID:sourceID, sinkID: 1})
+ smw.call("connect", JsonArg)
+ }
+ }
+ }
+ states: [
+ State {
+ when: player.playbackState === MediaPlayer.PlayingState
+ PropertyChanges {
+ target: play
+ offImage: './images/AGL_MediaPlayer_Player_Pause.svg'
+ onClicked: {
+ var JsonArg = JSON.stringify({mainConnectionID: connectionID})
+ console.log(JsonArg)
+ smw.call("disconnect",JsonArg)
+ }
+ }
+ },
+ State {
+ when: bluetooth.av_connected && bluetooth.state == "playing"
+ PropertyChanges {
+ target: play
+ offImage: './images/AGL_MediaPlayer_Player_Pause.svg'
+ onClicked: bluetooth.sendMediaCommand("Pause")
+ }
+ }
+
+ ]
+ }
+ ImageButton {
+ id: forward
+ offImage: './images/AGL_MediaPlayer_ForwardArrow.svg'
+ onClicked: {
+ if (bluetooth.av_connected) {
+ bluetooth.sendMediaCommand("Next")
+ } else {
+ playlist.next()
+ }
+ }
+ }
+
+ Item { Layout.fillWidth: true }
+
+ ToggleButton {
+ visible: bluetooth.connected
+ checked: bluetooth.av_connected
+ offImage: './images/AGL_MediaPlayer_Bluetooth_Inactive.svg'
+ onImage: './images/AGL_MediaPlayer_Bluetooth_Active.svg'
+
+ onClicked: {
+ if (bluetooth.av_connected) {
+ bluetooth.disconnect_profiles()
+ } else {
+ bluetooth.connect_profiles()
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ Item {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.preferredHeight: 407
+
+ PlaylistWithMetadata {
+ id: playlistmodel
+ source: playlist
+ }
+
+ ListView {
+ anchors.fill: parent
+ id: playlistview
+ visible: bluetooth.av_connected == false
+ clip: true
+ header: Label {
+ x: 50
+ text: 'PLAYLIST'
+ opacity: 0.5
+ }
+ model: playlistmodel
+ currentIndex: playlist.currentIndex
+
+ delegate: MouseArea {
+ id: delegate
+ width: ListView.view.width
+ height: ListView.view.height / 4
+ RowLayout {
+ anchors.fill: parent
+ anchors.leftMargin: 50
+ anchors.rightMargin: 50
+ Image {
+ source: model.coverArt
+ fillMode: Image.PreserveAspectFit
+ Layout.preferredWidth: delegate.height
+ Layout.preferredHeight: delegate.height
+ }
+ ColumnLayout {
+ Layout.fillWidth: true
+ Label {
+ Layout.fillWidth: true
+ text: model.title
+ }
+ Label {
+ Layout.fillWidth: true
+ text: model.artist
+ color: '#66FF99'
+ font.pixelSize: 32
+ }
+ }
+ Label {
+ text: player.time2str(model.duration)
+ color: '#66FF99'
+ font.pixelSize: 32
+ }
+ }
+ onClicked: {
+ playlist.currentIndex = model.index
+ sourceIndex = model.index;
+ console.log("mediaplayer: call connect")
+ var JsonArg = JSON.stringify({sourceID:sourceID, sinkID: 1})
+ smw.call("connect", JsonArg)
+ }
+ }
+
+ highlight: Rectangle {
+ color: 'white'
+ opacity: 0.25
+ }
+ }
+ }
+ Component.onCompleted: {
+ var media = JSON.stringify({appname:"mediaplayer"})
+ var bta = JSON.stringify({appname:"bluetooth_audio"})
+ smw.call("registerSource",media)
+ }
+ }
+}