bluetooth: add a2dp metadata and avrcp controls
[apps/mediaplayer.git] / app / MediaPlayer.qml
1 /*
2  * Copyright (C) 2016 The Qt Company Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 import QtQuick 2.6
18 import QtQuick.Layouts 1.1
19 import QtQuick.Controls 2.0
20 import QtMultimedia 5.6
21 import AGL.Demo.Controls 1.0
22 import MediaPlayer 1.0
23
24 ApplicationWindow {
25     id: root
26
27     Item {
28         id: bluetooth
29         property bool connected: false
30         property string state
31
32         property string artist
33         property string title
34         property int duration: 0
35         property int position: 0
36
37         function disableBluetooth() {
38             bluetooth.artist = ''
39             bluetooth.title = ''
40             bluetooth.duration = 0
41             bluetooth.position = 0
42             bluetooth.connected = false
43         }
44     }
45
46     Connections {
47         target: dbus
48
49         onProcessPlaylistUpdate: {
50             playlist.clear()
51             playlist.addItems(mediaFiles)
52
53             playlistmodel.setSource(playlist)
54             playlistview.visible = bluetooth.connected == false
55         }
56
57         onProcessPlaylistHide: {
58             playlistview.visible = false
59             player.stop()
60         }
61
62         onProcessPlaylistShow: {
63             playlistview.visible = true
64             bluetooth.disableBluetooth()
65         }
66
67         onDisplayBluetoothMetadata: {
68             if (avrcp_artist)
69                 bluetooth.artist = avrcp_artist
70             if (avrcp_title)
71                 bluetooth.title = avrcp_title
72             bluetooth.duration = avrcp_duration
73         }
74
75         onUpdatePlayerStatus: {
76             bluetooth.connected = true
77             bluetooth.state = status
78         }
79
80         onUpdatePosition: {
81             slider.value = current_position
82             bluetooth.position = current_position
83         }
84     }
85
86     MediaPlayer {
87         id: player
88         audioRole: MediaPlayer.MusicRole
89         autoLoad: true
90         playlist: playlist
91
92         property bool is_bluetooth: false
93         function time2str(value) {
94             return Qt.formatTime(new Date(value), 'mm:ss')
95         }
96         onPositionChanged: slider.value = player.position
97     }
98
99     Timer {
100         id: timer
101         interval: 250
102         running: (bluetooth.connected && bluetooth.state == "playing")
103         repeat: true
104         onTriggered: {
105             bluetooth.position = dbus.getCurrentPosition()
106             slider.value = bluetooth.position
107         }
108     }
109
110     Playlist {
111         id: playlist
112         playbackMode: random.checked ? Playlist.Random : loop.checked ? Playlist.Loop : Playlist.Sequential
113
114         Component.onCompleted: {
115             playlist.addItems(mediaFiles)
116         }
117     }
118
119
120     ColumnLayout {
121         anchors.fill: parent
122         Item {
123             Layout.fillWidth: true
124             Layout.fillHeight: true
125             Layout.preferredHeight: 1080
126             clip: true
127             Image {
128                 id: albumart
129                 anchors.left: parent.left
130                 anchors.right: parent.right
131                 anchors.bottom: parent.bottom
132                 height: sourceSize.height * width / sourceSize.width
133                 fillMode: Image.PreserveAspectCrop
134                 source: player.metaData.coverArtImage ? player.metaData.coverArtImage : ''
135                 visible: bluetooth.connected == false
136             }
137
138             Item {
139                 anchors.left: parent.left
140                 anchors.right: parent.right
141                 anchors.bottom: parent.bottom
142                 height :307
143                 Rectangle {
144                     anchors.fill: parent
145                     color: 'black'
146                     opacity: 0.75
147                 }
148
149                 ColumnLayout {
150                     anchors.fill: parent
151                     anchors.margins: root.width * 0.02
152                     Item {
153                         Layout.fillWidth: true
154                         Layout.fillHeight: true
155                         Row {
156                             spacing: 20
157                             ToggleButton {
158                                 id: random
159                                 visible: bluetooth.connected == false
160                                 offImage: './images/AGL_MediaPlayer_Shuffle_Inactive.svg'
161                                 onImage: './images/AGL_MediaPlayer_Shuffle_Active.svg'
162                             }
163                             ToggleButton {
164                                 id: loop
165                                 visible: bluetooth.connected == false
166                                 offImage: './images/AGL_MediaPlayer_Loop_Inactive.svg'
167                                 onImage: './images/AGL_MediaPlayer_Loop_Active.svg'
168                             }
169                         }
170                         ColumnLayout {
171                             anchors.fill: parent
172                             Label {
173                                 id: title
174                                 Layout.alignment: Layout.Center
175                                 text: bluetooth.title ? bluetooth.title : (player.metaData.title ? player.metaData.title : '')
176                                 horizontalAlignment: Label.AlignHCenter
177                                 verticalAlignment: Label.AlignVCenter
178                             }
179                             Label {
180                                 Layout.alignment: Layout.Center
181                                 text: bluetooth.artist ? bluetooth.artist : (player.metaData.contributingArtist ? player.metaData.contributingArtist : '')
182                                 horizontalAlignment: Label.AlignHCenter
183                                 verticalAlignment: Label.AlignVCenter
184                                 font.pixelSize: title.font.pixelSize * 0.6
185                             }
186                         }
187                     }
188                     Slider {
189                         id: slider
190                         Layout.fillWidth: true
191                         to: bluetooth.connected ? bluetooth.duration : player.duration
192                         enabled: bluetooth.connected == false
193                         function getPosition() {
194                             if (bluetooth.connected && bluetooth.position) {
195                                 return player.time2str(bluetooth.position)
196                             }
197                             return player.time2str(player.position)
198                         }
199                         Label {
200                             id: position
201                             anchors.left: parent.left
202                             anchors.bottom: parent.top
203                             font.pixelSize: 32
204                             text: slider.getPosition()
205                         }
206                         Label {
207                             id: duration
208                             anchors.right: parent.right
209                             anchors.bottom: parent.top
210                             font.pixelSize: 32
211                             text: bluetooth.connected ? player.time2str(bluetooth.duration) : player.time2str(player.duration)
212                         }
213                         onPressedChanged: player.seek(value)
214                     }
215                     RowLayout {
216                         Layout.fillHeight: true
217 //                        Image {
218 //                            source: './images/AGL_MediaPlayer_Playlist_Inactive.svg'
219 //                        }
220 //                        Image {
221 //                            source: './images/AGL_MediaPlayer_CD_Inactive.svg'
222 //                        }
223                         Item { Layout.fillWidth: true }
224                         ImageButton {
225                             id: previous
226                             offImage: './images/AGL_MediaPlayer_BackArrow.svg'
227                             onClicked: {
228                                 if (bluetooth.connected) {
229                                     dbus.processQMLEvent("Previous")
230                                 } else {
231                                     playlist.previous()
232                                 }
233                             }
234                         }
235                         ImageButton {
236                             id: play
237                             offImage: './images/AGL_MediaPlayer_Player_Play.svg'
238                             onClicked: {
239                                 if (bluetooth.connected) {
240                                     dbus.processQMLEvent("Play")
241                                 } else {
242                                     player.play()
243                                 }
244                             }
245                             states: [
246                                 State {
247                                     when: player.playbackState === MediaPlayer.PlayingState
248                                     PropertyChanges {
249                                         target: play
250                                         offImage: './images/AGL_MediaPlayer_Player_Pause.svg'
251                                         onClicked: player.pause()
252                                     }
253                                 },
254                                 State {
255                                     when: bluetooth.connected && bluetooth.state == "playing"
256                                     PropertyChanges {
257                                         target: play
258                                         offImage: './images/AGL_MediaPlayer_Player_Pause.svg'
259                                         onClicked: dbus.processQMLEvent("Pause")
260                                     }
261                                 }
262
263                             ]
264                         }
265                         ImageButton {
266                             id: forward
267                             offImage: './images/AGL_MediaPlayer_ForwardArrow.svg'
268                             onClicked: {
269                                 if (bluetooth.connected) {
270                                     dbus.processQMLEvent("Next")
271                                 } else {
272                                     playlist.next()
273                                 }
274                             }
275                         }
276
277                         Item { Layout.fillWidth: true }
278 //                        Image {
279 //                            source: './images/AGL_MediaPlayer_Bluetooth_Inactive.svg'
280 //                        }
281 //                        Image {
282 //                            source: './images/AGL_MediaPlayer_Radio_Inactive.svg'
283 //                        }
284                     }
285                 }
286             }
287         }
288         Item {
289             Layout.fillWidth: true
290             Layout.fillHeight: true
291             Layout.preferredHeight: 407
292
293             PlaylistWithMetadata {
294                 id: playlistmodel
295                 source: playlist
296             }
297
298             ListView {
299                 anchors.fill: parent
300                 id: playlistview
301                 clip: true
302                 header: Label {
303                     x: 50
304                     text: 'PLAYLIST'
305                     opacity: 0.5
306                 }
307                 model: playlistmodel
308                 currentIndex: playlist.currentIndex
309
310                 delegate: MouseArea {
311                     id: delegate
312                     width: ListView.view.width
313                     height: ListView.view.height / 4
314                     RowLayout {
315                         anchors.fill: parent
316                         anchors.leftMargin: 50
317                         anchors.rightMargin: 50
318                         Image {
319                             source: model.coverArt
320                             fillMode: Image.PreserveAspectFit
321                             Layout.preferredWidth: delegate.height
322                             Layout.preferredHeight: delegate.height
323                         }
324                         ColumnLayout {
325                             Layout.fillWidth: true
326                             Label {
327                                 Layout.fillWidth: true
328                                 text: model.title
329                             }
330                             Label {
331                                 Layout.fillWidth: true
332                                 text: model.artist
333                                 color: '#66FF99'
334                                 font.pixelSize: 32
335                             }
336                         }
337                         Label {
338                             text: player.time2str(model.duration)
339                             color: '#66FF99'
340                             font.pixelSize: 32
341                         }
342                     }
343                     onClicked: {
344                         playlist.currentIndex = model.index
345                         player.play()
346                     }
347                 }
348
349                 highlight: Rectangle {
350                     color: 'white'
351                     opacity: 0.25
352                 }
353             }
354         }
355     }
356 }