bluetooth: fix avrcp previous play position offset
[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         property int pos_offset: 0
37
38         function disableBluetooth() {
39             bluetooth.artist = ''
40             bluetooth.title = ''
41             bluetooth.duration = 0
42             bluetooth.position = 0
43             bluetooth.pos_offset = 0
44             bluetooth.connected = false
45         }
46     }
47
48     Connections {
49         target: dbus
50
51         onStopPlayback: {
52             player.stop()
53             playlist.clear()
54             playlistmodel.setSource(playlist)
55             playlistview.visible = false
56         }
57
58         onProcessPlaylistUpdate: {
59             playlist.clear()
60             playlist.addItems(mediaFiles)
61
62             playlistmodel.setSource(playlist)
63             playlistview.visible = bluetooth.connected == false
64         }
65
66         onProcessPlaylistHide: {
67             playlistview.visible = false
68             player.stop()
69         }
70
71         onProcessPlaylistShow: {
72             playlistview.visible = true
73             bluetooth.disableBluetooth()
74         }
75
76         onDisplayBluetoothMetadata: {
77             if (avrcp_artist)
78                 bluetooth.artist = avrcp_artist
79             if (avrcp_title)
80                 bluetooth.title = avrcp_title
81             bluetooth.duration = avrcp_duration
82             bluetooth.pos_offset = 0
83         }
84
85         onUpdatePlayerStatus: {
86             bluetooth.connected = true
87             bluetooth.state = status
88         }
89
90         onUpdatePosition: {
91             slider.value = current_position
92             bluetooth.position = current_position
93         }
94     }
95
96     MediaPlayer {
97         id: player
98         audioRole: MediaPlayer.MusicRole
99         autoLoad: true
100         playlist: playlist
101
102         property bool is_bluetooth: false
103         function time2str(value) {
104             return Qt.formatTime(new Date(value), 'mm:ss')
105         }
106         onPositionChanged: slider.value = player.position
107     }
108
109     Timer {
110         id: timer
111         interval: 250
112         running: (bluetooth.connected && bluetooth.state == "playing")
113         repeat: true
114         onTriggered: {
115             bluetooth.position = dbus.getCurrentPosition() - bluetooth.pos_offset
116             slider.value = bluetooth.position
117         }
118     }
119
120     Playlist {
121         id: playlist
122         playbackMode: random.checked ? Playlist.Random : loop.checked ? Playlist.Loop : Playlist.Sequential
123
124         Component.onCompleted: {
125             playlist.addItems(mediaFiles)
126         }
127     }
128
129
130     ColumnLayout {
131         anchors.fill: parent
132         Item {
133             Layout.fillWidth: true
134             Layout.fillHeight: true
135             Layout.preferredHeight: 1080
136             clip: true
137             Image {
138                 id: albumart
139                 anchors.left: parent.left
140                 anchors.right: parent.right
141                 anchors.bottom: parent.bottom
142                 height: sourceSize.height * width / sourceSize.width
143                 fillMode: Image.PreserveAspectCrop
144                 source: player.metaData.coverArtImage ? player.metaData.coverArtImage : ''
145                 visible: bluetooth.connected == false
146             }
147
148             Item {
149                 anchors.left: parent.left
150                 anchors.right: parent.right
151                 anchors.bottom: parent.bottom
152                 height :307
153                 Rectangle {
154                     anchors.fill: parent
155                     color: 'black'
156                     opacity: 0.75
157                 }
158
159                 ColumnLayout {
160                     anchors.fill: parent
161                     anchors.margins: root.width * 0.02
162                     Item {
163                         Layout.fillWidth: true
164                         Layout.fillHeight: true
165                         Row {
166                             spacing: 20
167                             ToggleButton {
168                                 id: random
169                                 visible: bluetooth.connected == false
170                                 offImage: './images/AGL_MediaPlayer_Shuffle_Inactive.svg'
171                                 onImage: './images/AGL_MediaPlayer_Shuffle_Active.svg'
172                             }
173                             ToggleButton {
174                                 id: loop
175                                 visible: bluetooth.connected == false
176                                 offImage: './images/AGL_MediaPlayer_Loop_Inactive.svg'
177                                 onImage: './images/AGL_MediaPlayer_Loop_Active.svg'
178                             }
179                         }
180                         ColumnLayout {
181                             anchors.fill: parent
182                             Label {
183                                 id: title
184                                 Layout.alignment: Layout.Center
185                                 text: bluetooth.title ? bluetooth.title : (player.metaData.title ? player.metaData.title : '')
186                                 horizontalAlignment: Label.AlignHCenter
187                                 verticalAlignment: Label.AlignVCenter
188                             }
189                             Label {
190                                 Layout.alignment: Layout.Center
191                                 text: bluetooth.artist ? bluetooth.artist : (player.metaData.contributingArtist ? player.metaData.contributingArtist : '')
192                                 horizontalAlignment: Label.AlignHCenter
193                                 verticalAlignment: Label.AlignVCenter
194                                 font.pixelSize: title.font.pixelSize * 0.6
195                             }
196                         }
197                     }
198                     Slider {
199                         id: slider
200                         Layout.fillWidth: true
201                         to: bluetooth.connected ? bluetooth.duration : player.duration
202                         enabled: bluetooth.connected == false
203                         function getPosition() {
204                             if (bluetooth.connected && bluetooth.position) {
205                                 return player.time2str(bluetooth.position)
206                             }
207                             return player.time2str(player.position)
208                         }
209                         Label {
210                             id: position
211                             anchors.left: parent.left
212                             anchors.bottom: parent.top
213                             font.pixelSize: 32
214                             text: slider.getPosition()
215                         }
216                         Label {
217                             id: duration
218                             anchors.right: parent.right
219                             anchors.bottom: parent.top
220                             font.pixelSize: 32
221                             text: bluetooth.connected ? player.time2str(bluetooth.duration) : player.time2str(player.duration)
222                         }
223                         onPressedChanged: player.seek(value)
224                     }
225                     RowLayout {
226                         Layout.fillHeight: true
227 //                        Image {
228 //                            source: './images/AGL_MediaPlayer_Playlist_Inactive.svg'
229 //                        }
230 //                        Image {
231 //                            source: './images/AGL_MediaPlayer_CD_Inactive.svg'
232 //                        }
233                         Item { Layout.fillWidth: true }
234                         ImageButton {
235                             id: previous
236                             offImage: './images/AGL_MediaPlayer_BackArrow.svg'
237                             onClicked: {
238                                 if (bluetooth.connected) {
239                                     bluetooth.pos_offset = dbus.getCurrentPosition()
240                                     dbus.processQMLEvent("Previous")
241                                 } else {
242                                     playlist.previous()
243                                 }
244                             }
245                         }
246                         ImageButton {
247                             id: play
248                             offImage: './images/AGL_MediaPlayer_Player_Play.svg'
249                             onClicked: {
250                                 if (bluetooth.connected) {
251                                     dbus.processQMLEvent("Play")
252                                 } else {
253                                     player.play()
254                                 }
255                             }
256                             states: [
257                                 State {
258                                     when: player.playbackState === MediaPlayer.PlayingState
259                                     PropertyChanges {
260                                         target: play
261                                         offImage: './images/AGL_MediaPlayer_Player_Pause.svg'
262                                         onClicked: player.pause()
263                                     }
264                                 },
265                                 State {
266                                     when: bluetooth.connected && bluetooth.state == "playing"
267                                     PropertyChanges {
268                                         target: play
269                                         offImage: './images/AGL_MediaPlayer_Player_Pause.svg'
270                                         onClicked: dbus.processQMLEvent("Pause")
271                                     }
272                                 }
273
274                             ]
275                         }
276                         ImageButton {
277                             id: forward
278                             offImage: './images/AGL_MediaPlayer_ForwardArrow.svg'
279                             onClicked: {
280                                 if (bluetooth.connected) {
281                                     dbus.processQMLEvent("Next")
282                                 } else {
283                                     playlist.next()
284                                 }
285                             }
286                         }
287
288                         Item { Layout.fillWidth: true }
289 //                        Image {
290 //                            source: './images/AGL_MediaPlayer_Bluetooth_Inactive.svg'
291 //                        }
292 //                        Image {
293 //                            source: './images/AGL_MediaPlayer_Radio_Inactive.svg'
294 //                        }
295                     }
296                 }
297             }
298         }
299         Item {
300             Layout.fillWidth: true
301             Layout.fillHeight: true
302             Layout.preferredHeight: 407
303
304             PlaylistWithMetadata {
305                 id: playlistmodel
306                 source: playlist
307             }
308
309             ListView {
310                 anchors.fill: parent
311                 id: playlistview
312                 clip: true
313                 header: Label {
314                     x: 50
315                     text: 'PLAYLIST'
316                     opacity: 0.5
317                 }
318                 model: playlistmodel
319                 currentIndex: playlist.currentIndex
320
321                 delegate: MouseArea {
322                     id: delegate
323                     width: ListView.view.width
324                     height: ListView.view.height / 4
325                     RowLayout {
326                         anchors.fill: parent
327                         anchors.leftMargin: 50
328                         anchors.rightMargin: 50
329                         Image {
330                             source: model.coverArt
331                             fillMode: Image.PreserveAspectFit
332                             Layout.preferredWidth: delegate.height
333                             Layout.preferredHeight: delegate.height
334                         }
335                         ColumnLayout {
336                             Layout.fillWidth: true
337                             Label {
338                                 Layout.fillWidth: true
339                                 text: model.title
340                             }
341                             Label {
342                                 Layout.fillWidth: true
343                                 text: model.artist
344                                 color: '#66FF99'
345                                 font.pixelSize: 32
346                             }
347                         }
348                         Label {
349                             text: player.time2str(model.duration)
350                             color: '#66FF99'
351                             font.pixelSize: 32
352                         }
353                     }
354                     onClicked: {
355                         playlist.currentIndex = model.index
356                         player.play()
357                     }
358                 }
359
360                 highlight: Rectangle {
361                     color: 'white'
362                     opacity: 0.25
363                 }
364             }
365         }
366     }
367 }