Merge lp:~ahayzen/music-app/mediaplayer-simplify into lp:music-app/trusty

Proposed by Andrew Hayzen
Status: Merged
Approved by: Andrew Hayzen
Approved revision: 324
Merged at revision: 369
Proposed branch: lp:~ahayzen/music-app/mediaplayer-simplify
Merge into: lp:music-app/trusty
Diff against target: 1531 lines (+397/-453)
14 files modified
LibraryListModel.qml (+2/-0)
MusicAlbums.qml (+1/-0)
MusicNowPlaying.qml (+20/-15)
MusicPlaylists.qml (+0/-7)
MusicStart.qml (+1/-0)
MusicToolbar.qml (+50/-71)
MusicTracks.qml (+0/-5)
Player.qml (+196/-0)
common/AlbumsSheet.qml (+0/-4)
common/BlurredBackground.qml (+3/-1)
music-app.qml (+61/-294)
tests/autopilot/music_app/emulators.py (+3/-0)
tests/autopilot/music_app/tests/__init__.py (+4/-0)
tests/autopilot/music_app/tests/test_music.py (+56/-56)
To merge this branch: bzr merge lp:~ahayzen/music-app/mediaplayer-simplify
Reviewer Review Type Date Requested Status
Victor Thompson Approve
Ubuntu Phone Apps Jenkins Bot continuous-integration Approve
Nicholas Skaggs (community) Approve
Review via email: mp+201265@code.launchpad.net

Commit message

* Make it easier to change shuffle/repeat
* Move MediaPlayer into its own file
* Simplify bindings to MediaPlayer by using Connections

Description of the change

I know we prefer to write new features before refactoring code.
However when developing the keyboard shortcuts I noticed how difficult it had become to, for example, toggle shuffle. The code from the keyboard shortcut would have to:
* Update the Settings
* Update the global variable
* Find any UI items (eg in the toolbar) and toggle them

The following merge moves the MediaPlayer component into its own file, I have then removed the signals and code that talks to 'external' components, so this file should only access the MediaPlayer, trackQueue and Settings. Any 'external' components use Connections to 'listen' for changes in the MediaPlayer.

This has resulted in making it easier to manage, for example toggling shuffle now just requires
player.shuffle = !player.shuffle

Furthermore this simplifies loads more code and makes things easier to read/find.

To post a comment you must log in.
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
304. By Andrew Hayzen

* Fix autopilot

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
305. By Andrew Hayzen

* Fix for autopilot

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
306. By Andrew Hayzen

* Merge of trunk

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Nicholas Skaggs (nskaggs) wrote :

Seems you found an issue with libautopilot-qt; can you link the bug here Andrew?

Revision history for this message
Nicholas Skaggs (nskaggs) wrote :

Waiting for https://code.launchpad.net/~thomir/autopilot-qt/export-qobject/+merge/201865 to land which fixes the introspection issue.

307. By Andrew Hayzen

* Empty commit

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
308. By Andrew Hayzen

* Merge of trunk

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
309. By Andrew Hayzen

* Fixes for trackQueue empty

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
310. By Andrew Hayzen

* Fix for labels

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Victor Thompson (vthompson) wrote :

Couldn't you just put the MediaPlayer in an Item and have properties defined under the item? That's the way the Qt documentation lists an example [1]. This is also how many of the Ubuntu SDK items are constructed and I can imagine it's probably the preferred way of creating custom components. You can alias the MediaPlayers properties and define signals for play()/pause/etc.

[1] http://qt-project.org/doc/qt-5.0/qtmultimedia/qml-qtmultimedia5-mediaplayer.html#details
[2] http://bazaar.launchpad.net/~ubuntu-sdk-team/ubuntu-ui-toolkit/trunk/files/head:/modules/Ubuntu/Components/

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
311. By Andrew Hayzen

* Merge of trunk

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Nicholas Skaggs (nskaggs) wrote :

Are we still trying to merge this, or going a different way now?

Revision history for this message
Andrew Hayzen (ahayzen) wrote :

Think i'm gonna modify it to work within the limitations found in autopilot.

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Nicholas Skaggs (nskaggs) wrote :

I've lost the bug this was linked to, do you have it handy?

Revision history for this message
Nicholas Skaggs (nskaggs) wrote :
312. By Andrew Hayzen

* Merge of trunk

313. By Andrew Hayzen

* Fixes for currentIndex changes

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Nicholas Skaggs (nskaggs) wrote :

The last bits of fixes for the autopilot bug affecting this should land by midweek.

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
314. By Andrew Hayzen

* Merge of trunk

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
315. By Andrew Hayzen

* Embed MediaPlayer inside Item and use alias to access properties

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
316. By Andrew Hayzen

* Add Player.qml to cmake

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Nicholas Skaggs (nskaggs) wrote :

In my cmake wisdom, I think we can simplify how the qml files are specified; this should work;

file(GLOB SRC_FILES
     RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
     *.qml *.js *.png *.json)
install(FILES ${SRC_FILES} DESTINATION ${DATA_DIR})

Add the *.filetype for whatever is needed

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
317. By Andrew Hayzen

* Merge of trunk

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Victor Thompson (vthompson) wrote :

Doing some initial testing on the Desktop (I'll try to do more on the phone later). It seems as through removing items from the queue is causing some item dividers to be left on the screen (see attached). Otherwise, functionally, I think things are in place. http://i.imgur.com/MvgsmXm.png

318. By Andrew Hayzen

* Fix for currentIndex not referenced correctly

Revision history for this message
Andrew Hayzen (ahayzen) wrote :

Good catch I had missed a conversion of one of the properties in the removal function, please retest :)

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Nicholas Skaggs (nskaggs) wrote :

This branch needs to merge for this to work. It was approved and readied a bit ago, but they seemed to have backed off on it:

https://code.launchpad.net/~veebers/autopilot-qt/reintroduce-exporting-qobject-children-of-qml-items/+merge/207581

Revision history for this message
Nicholas Skaggs (nskaggs) wrote :

Per thomi;

<thomi> balloons: it changes the order of objects exported by select_many, and several test suites rely (incorrectly) on that order
<thomi> so we cannot release it until everyone updates their tests
* balloons facepalms
<thomi> we're planning on building it into a PPA, so people can update their suites more easily
<thomi> ... we're still committed to getting it out

With that info, we need to decide what we're going to do with this and how to land it

Revision history for this message
Nicholas Skaggs (nskaggs) wrote :

My 2 cents, fyi, I would push for us to merge this if everyone is ready, and add a skip for the test. ::shocked?:: :-) We'll manually verify this feature before release and unskip the test once the new version of AP lands. We'll need to reference the bug and make sure we follow it.

review: Approve
Revision history for this message
Andrew Hayzen (ahayzen) wrote :

Do we have a timescale for this conversion to take place? eg days, weeks, months? As I feel this would affect our decision.

Revision history for this message
Nicholas Skaggs (nskaggs) wrote :

No ETA at this point; and I hesitate to give one since it's been stretched out so much. That said, the AP team is actively trying to land this but they are depending on others to clean up there tests so they don't experience breakage. If this can sit for 2 more weeks or so then it's an option. I'm assuming you wish to land it sooner rather than later and I don't like hings sitting out of trunk for so long; will this continue to cause re-work to rebase to trunk?

319. By Andrew Hayzen

* Fix for missed case of nextSong, stopSong conversion

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
320. By Andrew Hayzen

* Remove use of key-value array in player

321. By Andrew Hayzen

* Merge of trunk

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
322. By Andrew Hayzen

* Fixed missed cases

323. By Andrew Hayzen

* Fix for autopilot

Revision history for this message
Andrew Hayzen (ahayzen) wrote :

OK after discussions with Victor, we have decided to split the currentMeta dictionary into its separate properties (eg currentMetaTitle) so that it will pass the autopilot tests. Once the autopilot fixes land this can then be reinvestigated to whether it is better to have them as split or together.

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
324. By Andrew Hayzen

* Fix for autopilot

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Victor Thompson (vthompson) wrote :

Approved. I'm glad we could resolve this in an efficient manner. Thanks for all the work here Andrew and Nicholas!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'LibraryListModel.qml'
2--- LibraryListModel.qml 2014-01-30 19:54:41 +0000
3+++ LibraryListModel.qml 2014-03-07 16:40:44 +0000
4@@ -87,6 +87,8 @@
5
6 function indexOf(file)
7 {
8+ file = file.toString();
9+
10 if (file.indexOf("file://") == 0)
11 {
12 file = file.slice(7, file.length)
13
14=== modified file 'MusicAlbums.qml'
15--- MusicAlbums.qml 2014-01-16 19:22:52 +0000
16+++ MusicAlbums.qml 2014-03-07 16:40:44 +0000
17@@ -133,6 +133,7 @@
18 }
19 onClicked: {
20 albumTracksModel.filterAlbumTracks(album)
21+
22 songsSheet.line1 = artist
23 songsSheet.line2 = album
24 songsSheet.isAlbum = true
25
26=== modified file 'MusicNowPlaying.qml'
27--- MusicNowPlaying.qml 2014-02-22 22:12:21 +0000
28+++ MusicNowPlaying.qml 2014-03-07 16:40:44 +0000
29@@ -65,10 +65,25 @@
30 }
31
32 Component.onCompleted: {
33- onPlayingTrackChange.connect(updateCurrentIndex)
34 onToolbarShownChanged.connect(jumpToCurrent)
35 }
36
37+ Connections {
38+ target: player
39+ onCurrentIndexChanged: {
40+ if (player.source === "") {
41+ return;
42+ }
43+
44+ // Collapse currently expanded track and the new current
45+ collapseExpand(queuelist.currentIndex);
46+ queuelist.currentIndex = player.currentIndex;
47+ collapseExpand(queuelist.currentIndex);
48+
49+ customdebug("MusicQueue update currentIndex: " + player.source);
50+ }
51+ }
52+
53 function jumpToCurrent(shown, currentPage, currentTab)
54 {
55 // If the toolbar is shown, the page is now playing and snaptrack is enabled
56@@ -79,16 +94,6 @@
57 }
58 }
59
60- function updateCurrentIndex(file)
61- {
62- // Collapse currently expanded track and the new current
63- collapseExpand(queuelist.currentIndex);
64- queuelist.currentIndex = currentIndex;
65- collapseExpand(queuelist.currentIndex);
66-
67- customdebug("MusicQueue update currentIndex: " + file);
68- }
69-
70 ListView {
71 id: queuelist
72 objectName: "queuelist"
73@@ -469,16 +474,16 @@
74 if (queuelist.count > 1)
75 {
76 // Next song and only play if currently playing
77- nextSong(isPlaying);
78+ player.nextSong(player.isPlaying);
79 }
80 else
81 {
82- stopSong();
83+ player.stop();
84 }
85 }
86
87- if (index < currentIndex) {
88- currentIndex -= 1;
89+ if (index < player.currentIndex) {
90+ player.currentIndex -= 1;
91 }
92
93 // Remove item from queue and clear caches
94
95=== modified file 'MusicPlaylists.qml'
96--- MusicPlaylists.qml 2014-01-16 19:22:52 +0000
97+++ MusicPlaylists.qml 2014-03-07 16:40:44 +0000
98@@ -42,13 +42,6 @@
99 property string oldPlaylistID: ""
100 property string inPlaylist: ""
101
102- Component.onCompleted: {
103- random = Settings.getSetting("shuffle") == "1" // shuffle state
104- scrobble = Settings.getSetting("scrobble") == "1" // scrobble state
105- lastfmusername = Settings.getSetting("lastfmusername") // lastfm username
106- lastfmpassword = Settings.getSetting("lastfmpassword") // lastfm password
107- }
108-
109 onVisibleChanged: {
110 if (visible === true)
111 {
112
113=== modified file 'MusicStart.qml'
114--- MusicStart.qml 2014-02-20 01:28:22 +0000
115+++ MusicStart.qml 2014-03-07 16:40:44 +0000
116@@ -187,6 +187,7 @@
117 } else {
118 albumTracksModel.filterAlbumTracks(title)
119 }
120+
121 songsSheet.line1 = title2
122 songsSheet.line2 = title
123 songsSheet.covers = recentItem.covers
124
125=== modified file 'MusicToolbar.qml'
126--- MusicToolbar.qml 2014-02-22 19:59:15 +0000
127+++ MusicToolbar.qml 2014-03-07 16:40:44 +0000
128@@ -262,7 +262,7 @@
129 id: musicToolbarPlayerControls
130 anchors.fill: parent
131 color: styleMusic.playerControls.backgroundColor
132- state: trackQueue.isEmpty === true ? "disabled" : "enabled"
133+ state: trackQueue.model.count === 0 ? "disabled" : "enabled"
134
135 states: [
136 State {
137@@ -293,7 +293,7 @@
138 id: disabledPlayerControlsGroup
139 anchors.fill: parent
140 color: "transparent"
141- visible: trackQueue.isEmpty === true
142+ visible: trackQueue.model.count === 0
143
144 Label {
145 id: noSongsInQueueLabel
146@@ -319,7 +319,7 @@
147 id: enabledPlayerControlsGroup
148 anchors.fill: parent
149 color: "transparent"
150- visible: trackQueue.isEmpty === false
151+ visible: trackQueue.model.count !== 0
152
153 /* Settings button */
154 // TODO: Enable settings when it is practical
155@@ -454,14 +454,7 @@
156
157 if (accepted)
158 {
159- if (player.playbackState === MediaPlayer.PlayingState)
160- {
161- player.pause()
162- }
163- else
164- {
165- player.play()
166- }
167+ player.toggle();
168 }
169 }
170 }
171@@ -496,7 +489,8 @@
172 elide: Text.ElideRight
173 fontSize: "medium"
174 objectName: "playercontroltitle"
175- text: mainView.currentTracktitle === "" ? mainView.currentFile : mainView.currentTracktitle
176+ text: player.currentMetaTitle === ""
177+ ? player.source : player.currentMetaTitle
178 }
179
180 /* Artist of track */
181@@ -510,7 +504,7 @@
182 color: styleMusic.playerControls.labelColor
183 elide: Text.ElideRight
184 fontSize: "small"
185- text: mainView.currentArtist
186+ text: player.currentMetaArtist
187 }
188
189 /* Album of track */
190@@ -524,7 +518,7 @@
191 color: styleMusic.playerControls.labelColor
192 elide: Text.ElideRight
193 fontSize: "small"
194- text: mainView.currentAlbum
195+ text: player.currentMetaAlbum
196 }
197 }
198 }
199@@ -574,7 +568,7 @@
200 elide: Text.ElideRight
201 fontSize: "medium"
202 objectName: "playercontroltitle"
203- text: trackQueue.isEmpty ? "" : mainView.currentTracktitle === "" ? mainView.currentFile : mainView.currentTracktitle
204+ text: trackQueue.model.count === 0 ? "" : player.currentMetaTitle === "" ? player.currentMetaFile : player.currentMetaTitle
205 }
206
207 /* Artist of track */
208@@ -589,7 +583,7 @@
209 color: styleMusic.playerControls.labelColor
210 elide: Text.ElideRight
211 fontSize: "small"
212- text: trackQueue.isEmpty ? "" : mainView.currentArtist
213+ text: trackQueue.model.count === 0 ? "" : player.currentMetaArtist
214 }
215
216 /* Album of track */
217@@ -604,7 +598,7 @@
218 color: styleMusic.playerControls.labelColor
219 elide: Text.ElideRight
220 fontSize: "small"
221- text: trackQueue.isEmpty ? "" : mainView.currentAlbum
222+ text: trackQueue.model.count === 0 ? "" : player.currentMetaAlbum
223 }
224 }
225 /* Clicking in the area shows the queue */
226@@ -612,7 +606,7 @@
227 anchors {
228 fill: nowPlayingWideAspectLabels
229 }
230- enabled: !trackQueue.isEmpty
231+ enabled: trackQueue.model.count !== 0
232 onClicked: {
233 nowPlaying.visible = true;
234 }
235@@ -626,6 +620,7 @@
236 anchors.rightMargin: units.gu(1)
237 anchors.verticalCenter: parent.verticalCenter
238 height: units.gu(6)
239+ opacity: player.repeat ? 1 : .4
240 width: height
241
242 Image {
243@@ -636,7 +631,7 @@
244 anchors.horizontalCenter: parent.horizontalCenter
245 source: Qt.resolvedUrl("images/media-playlist-repeat.svg")
246 verticalAlignment: Text.AlignVCenter
247- opacity: Settings.getSetting("repeat") === "1" ? 1 : .4
248+ opacity: player.repeat ? 1 : .4
249 }
250
251 MouseArea {
252@@ -644,9 +639,7 @@
253
254 onClicked: {
255 // Invert repeat settings
256- Settings.setSetting("repeat", !(Settings.getSetting("repeat") === "1"))
257- console.debug("Repeat:", Settings.getSetting("repeat") === "1")
258- repeatIcon.opacity = Settings.getSetting("repeat") === "1" ? 1 : .4
259+ player.repeat = !player.repeat
260 }
261 }
262 }
263@@ -659,7 +652,7 @@
264 anchors.verticalCenter: parent.verticalCenter
265 height: units.gu(6)
266 objectName: "previousshape"
267- opacity: trackQueue.isEmpty ? .4 : 1
268+ opacity: trackQueue.model.count === 0 ? .4 : 1
269 width: height
270
271 Image {
272@@ -677,7 +670,7 @@
273 id: nowPlayingPreviousMouseArea
274 onClicked:
275 {
276- previousSong()
277+ player.previousSong()
278 }
279 }
280 }
281@@ -751,7 +744,7 @@
282 width: height
283 anchors.horizontalCenter: parent.horizontalCenter
284 anchors.verticalCenter: parent.verticalCenter
285- opacity: trackQueue.isEmpty ? .4 : 1
286+ opacity: trackQueue.model.count === 0 ? .4 : 1
287 source: player.playbackState === MediaPlayer.PlayingState ?
288 Qt.resolvedUrl("images/media-playback-pause.svg") : Qt.resolvedUrl("images/media-playback-start.svg")
289 }
290@@ -779,14 +772,7 @@
291 return;
292 }
293
294- if (player.playbackState === MediaPlayer.PlayingState)
295- {
296- player.pause()
297- }
298- else
299- {
300- player.play()
301- }
302+ player.toggle();
303 }
304 }
305 }
306@@ -804,7 +790,7 @@
307 anchors.verticalCenter: parent.verticalCenter
308 height: units.gu(6)
309 objectName: "forwardshape"
310- opacity: trackQueue.isEmpty ? .4 : 1
311+ opacity: trackQueue.model.count === 0 ? .4 : 1
312 width: height
313
314 Image {
315@@ -822,7 +808,7 @@
316 id: nowPlayingNextMouseArea
317 onClicked:
318 {
319- nextSong()
320+ player.nextSong()
321 }
322 }
323 }
324@@ -835,6 +821,7 @@
325 anchors.leftMargin: units.gu(1)
326 anchors.verticalCenter: parent.verticalCenter
327 height: units.gu(6)
328+ opacity: player.shuffle ? 1 : .4
329 width: height
330
331 Image {
332@@ -844,7 +831,7 @@
333 anchors.verticalCenter: parent.verticalCenter
334 anchors.horizontalCenter: parent.horizontalCenter
335 source: Qt.resolvedUrl("images/media-playlist-shuffle.svg")
336- opacity: Settings.getSetting("shuffle") === "1" ? 1 : .4
337+ opacity: player.shuffle ? 1 : .4
338 }
339
340 MouseArea {
341@@ -852,11 +839,7 @@
342
343 onClicked: {
344 // Invert shuffle settings
345- mainView.random = !mainView.random
346- shuffleIcon.opacity = mainView.random ? 1 : .4
347- Settings.setSetting("shuffle", mainView.random)
348- console.debug("Shuffle:", Settings.getSetting("shuffle") === "1")
349-
350+ player.shuffle = !player.shuffle
351 }
352 }
353 }
354@@ -917,7 +900,7 @@
355 fontSize: "x-small"
356 height: parent.height
357 horizontalAlignment: Text.AlignHCenter
358- text: player.positionStr
359+ text: durationToString(player.position)
360 verticalAlignment: Text.AlignVCenter
361 width: units.gu(3)
362 }
363@@ -932,7 +915,7 @@
364 anchors.verticalCenter: parent.verticalCenter
365 color: "transparent"
366 height: units.gu(1);
367- state: trackQueue.isEmpty === true ? "disabled" : "enabled"
368+ state: trackQueue.model.count === 0 ? "disabled" : "enabled"
369
370 states: [
371 State {
372@@ -967,19 +950,23 @@
373 }
374 ]
375
376- // Connection from positionChanged signal
377- function updatePosition(position, duration)
378- {
379- if (player.seeking == false)
380- {
381- musicToolbarFullProgressHandle.x = ((position / duration) * musicToolbarFullProgressBarContainer.width)
382- - musicToolbarFullProgressHandle.width / 2;
383+ property bool seeking: false
384+
385+ onSeekingChanged: {
386+ if (seeking === false) {
387+ musicToolbarFullPositionLabel.text = durationToString(player.position)
388 }
389 }
390
391- Component.onCompleted: {
392- // Connect to signal from MediaPlayer
393- player.positionChange.connect(updatePosition)
394+ Connections {
395+ target: player
396+ onPositionChanged: {
397+ if (musicToolbarFullProgressBarContainer.seeking === false)
398+ {
399+ musicToolbarFullProgressHandle.x = (player.position / player.duration) * musicToolbarFullProgressBarContainer.width
400+ - musicToolbarFullProgressHandle.width / 2;
401+ }
402+ }
403 }
404
405 // Black background behind the progress bar
406@@ -1016,14 +1003,7 @@
407 // On X change update the position string
408 onXChanged: {
409 var fraction = (x + (width / 2)) / parent.width;
410- player.positionStr = __durationToString(fraction * player.duration);
411- }
412-
413- transitions: Transition {
414- NumberAnimation {
415- properties: "x"
416- duration: 1000
417- }
418+ musicToolbarFullPositionLabel.text = durationToString(fraction * player.duration)
419 }
420 }
421 }
422@@ -1038,7 +1018,7 @@
423 fontSize: "x-small"
424 height: parent.height
425 horizontalAlignment: Text.AlignHCenter
426- text: player.durationStr
427+ text: durationToString(player.duration)
428 verticalAlignment: Text.AlignVCenter
429 width: units.gu(3)
430 }
431@@ -1072,13 +1052,11 @@
432 height: parent.height
433 width: 0
434
435- function updatePosition(position, duration)
436- {
437- musicToolbarSmallProgressHint.width = (position / duration) * musicToolbarSmallProgressBackground.width
438- }
439-
440- Component.onCompleted: {
441- player.positionChange.connect(updatePosition)
442+ Connections {
443+ target: player
444+ onPositionChanged: {
445+ musicToolbarSmallProgressHint.width = (player.position / player.duration) * musicToolbarSmallProgressBackground.width
446+ }
447 }
448 }
449 }
450@@ -1191,7 +1169,8 @@
451 drag.target: musicToolbarFullProgressHandle
452
453 onPressed: {
454- player.seeking = true;
455+ musicToolbarFullProgressBarContainer.seeking = true;
456+
457 // Jump the handle to the current mouse position
458 musicToolbarFullProgressHandle.x = mouse.x - (musicToolbarFullProgressHandle.width / 2);
459 }
460@@ -1204,7 +1183,7 @@
461 fraction = fraction > 1 ? 1 : fraction
462
463 player.seek((fraction) * player.duration);
464- player.seeking = false;
465+ musicToolbarFullProgressBarContainer.seeking = false;
466 }
467 }
468
469
470=== modified file 'MusicTracks.qml'
471--- MusicTracks.qml 2014-02-24 00:38:11 +0000
472+++ MusicTracks.qml 2014-03-07 16:40:44 +0000
473@@ -49,11 +49,6 @@
474 highlightFollowsCurrentItem: false
475 model: libraryModel.model
476 delegate: trackDelegate
477- onCountChanged: {
478- //customdebug("onCountChanged: " + tracklist.count) // activate later
479- tracklist.currentIndex = libraryModel.indexOf(currentFile)
480- }
481-
482 Component {
483 id: trackDelegate
484 ListItem.Standard {
485
486=== added file 'Player.qml'
487--- Player.qml 1970-01-01 00:00:00 +0000
488+++ Player.qml 2014-03-07 16:40:44 +0000
489@@ -0,0 +1,196 @@
490+/*
491+ * Copyright (C) 2013 Andrew Hayzen <ahayzen@gmail.com>
492+ * Daniel Holm <d.holmen@gmail.com>
493+ * Victor Thompson <victor.thompson@gmail.com>
494+ *
495+ * This program is free software; you can redistribute it and/or modify
496+ * it under the terms of the GNU General Public License as published by
497+ * the Free Software Foundation; version 3.
498+ *
499+ * This program is distributed in the hope that it will be useful,
500+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
501+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
502+ * GNU General Public License for more details.
503+ *
504+ * You should have received a copy of the GNU General Public License
505+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
506+ */
507+
508+import QtQuick 2.0
509+import QtMultimedia 5.0
510+import QtQuick.LocalStorage 2.0
511+import "settings.js" as Settings
512+
513+/*
514+ * This file should *only* manage the media playing and the relevant settings
515+ * It should therefore only access MediaPlayer, trackQueue and Settings
516+ * Anything else within the app should use Connections to listen for changes
517+ */
518+
519+
520+Item {
521+ objectName: "player"
522+
523+ property string currentMetaAlbum: ""
524+ property string currentMetaArtist: ""
525+ property string currentMetaCover: ""
526+ property string currentMetaFile: ""
527+ property string currentMetaGenre: ""
528+ property string currentMetaLength: ""
529+ property string currentMetaTitle: ""
530+ property string currentMetaYear: ""
531+ property int currentIndex: -1
532+ property alias duration: mediaPlayer.duration
533+ property bool isPlaying: player.playbackState === MediaPlayer.PlayingState
534+ property alias playbackState: mediaPlayer.playbackState
535+ property alias position: mediaPlayer.position
536+ property bool repeat: Settings.getSetting("repeat") === "1"
537+ property bool shuffle: Settings.getSetting("shuffle") === "1"
538+ property alias source: mediaPlayer.source
539+
540+ onRepeatChanged: {
541+ Settings.setSetting("repeat", repeat ? "1" : "0")
542+ console.debug("Repeat:", Settings.getSetting("repeat") === "1")
543+ }
544+
545+ onShuffleChanged: {
546+ Settings.setSetting("shuffle", shuffle ? "1" : "0")
547+ console.debug("Shuffle:", Settings.getSetting("shuffle") === "1")
548+ }
549+
550+ function getSong(direction, startPlaying, fromControls) {
551+ // Seek to start if threshold reached when selecting previous
552+ if (direction === -1 && (player.position / 1000) > 5)
553+ {
554+ player.seek(0); // seek to start
555+ return;
556+ }
557+
558+ if (trackQueue.model.count == 0)
559+ {
560+ customdebug("No tracks in queue.");
561+ return;
562+ }
563+
564+ // default fromControls and startPlaying to true
565+ fromControls = fromControls === undefined ? true : fromControls;
566+ startPlaying = startPlaying === undefined ? true : startPlaying;
567+ var newIndex;
568+
569+ console.log("currentIndex: " + currentIndex)
570+ console.log("trackQueue.count: " + trackQueue.model.count)
571+
572+ if (shuffle) {
573+ var now = new Date();
574+ var seed = now.getSeconds();
575+
576+ // trackQueue must be above 1 otherwise an infinite loop will occur
577+ do {
578+ newIndex = (Math.floor((trackQueue.model.count)
579+ * Math.random(seed)));
580+ } while (newIndex === currentIndex && trackQueue.model.count > 1)
581+ } else {
582+ if ((currentIndex < trackQueue.model.count - 1 && direction === 1 )
583+ || (currentIndex > 0 && direction === -1)) {
584+ newIndex = currentIndex + direction
585+ } else if(direction === 1 && (Settings.getSetting("repeat") === "1"
586+ || fromControls)) {
587+ newIndex = 0
588+ } else if(direction === -1 && (Settings.getSetting("repeat") === "1" || fromControls)) {
589+ newIndex = trackQueue.model.count - 1
590+ }
591+ else
592+ {
593+ player.stop()
594+ return;
595+ }
596+ }
597+
598+ if (startPlaying) { // only start the track if told
599+ playSong(trackQueue.model.get(newIndex).file, newIndex)
600+ }
601+ else {
602+ currentIndex = newIndex
603+ source = Qt.resolvedUrl(trackQueue.model.get(newIndex).file)
604+ }
605+ }
606+
607+ function nextSong(startPlaying, fromControls) {
608+ getSong(1, startPlaying, fromControls)
609+ }
610+
611+ function pause() {
612+ mediaPlayer.pause();
613+ }
614+
615+ function play() {
616+ mediaPlayer.play();
617+ }
618+
619+ function playSong(filepath, index) {
620+ player.stop();
621+ currentIndex = index;
622+ player.source = Qt.resolvedUrl(filepath);
623+ player.play();
624+ }
625+
626+ function previousSong(startPlaying) {
627+ getSong(-1, startPlaying)
628+ }
629+
630+ function seek(position) {
631+ mediaPlayer.seek(position);
632+ }
633+
634+ function stop() {
635+ mediaPlayer.stop();
636+ }
637+
638+ function toggle() {
639+ if (player.playbackState == MediaPlayer.PlayingState) {
640+ player.pause()
641+ }
642+ else {
643+ player.play()
644+ }
645+ }
646+
647+ MediaPlayer {
648+ id: mediaPlayer
649+ muted: false
650+
651+ onSourceChanged: {
652+ // Force invalid source to ""
653+ if (source === undefined || source === false) {
654+ source = ""
655+ return
656+ }
657+
658+ if (source === "") {
659+ currentIndex = -1
660+ player.stop()
661+ }
662+ else {
663+ var obj = trackQueue.model.get(player.currentIndex);
664+ currentMetaAlbum = obj.album;
665+ currentMetaArtist = obj.artist;
666+ currentMetaCover = obj.cover;
667+ currentMetaFile = obj.file;
668+ currentMetaGenre = obj.genre;
669+ currentMetaLength = obj.length;
670+ currentMetaTitle = obj.title;
671+ currentMetaYear = obj.year;
672+ }
673+
674+ console.log("Source: " + source.toString())
675+ console.log("Index: " + currentIndex)
676+ }
677+
678+ onStatusChanged: {
679+ if (status == MediaPlayer.EndOfMedia) {
680+ nextSong(true, false) // next track
681+ }
682+ }
683+ }
684+}
685+
686
687=== modified file 'common/AlbumsSheet.qml'
688--- common/AlbumsSheet.qml 2014-03-06 17:23:56 +0000
689+++ common/AlbumsSheet.qml 2014-03-07 16:40:44 +0000
690@@ -51,10 +51,6 @@
691 model: artistAlbumsModel.model
692 delegate: albumTracksDelegate
693
694- onCountChanged: {
695- albumtrackslist.currentIndex = albumTracksModel.indexOf(currentFile)
696- }
697-
698 Component {
699 id: albumTracksDelegate
700
701
702=== modified file 'common/BlurredBackground.qml'
703--- common/BlurredBackground.qml 2013-12-15 05:28:37 +0000
704+++ common/BlurredBackground.qml 2014-03-07 16:40:44 +0000
705@@ -23,7 +23,9 @@
706 // Blurred background
707 Rectangle {
708 anchors.fill: parent
709- property string cover: mainView.currentCoverFull
710+ property string cover: player.currentMetaCover !== "" ?
711+ player.currentMetaCover :
712+ "images/cover_default.png"
713 // the album art
714 Image {
715 id: backgroundImage
716
717=== modified file 'music-app.qml'
718--- music-app.qml 2014-03-06 21:13:48 +0000
719+++ music-app.qml 2014-03-07 16:40:44 +0000
720@@ -83,7 +83,7 @@
721 id: nextAction
722 text: i18n.tr("Next")
723 keywords: i18n.tr("Next Track")
724- onTriggered: nextSong()
725+ onTriggered: player.nextSong()
726 }
727 Action {
728 id: playsAction
729@@ -91,13 +91,7 @@
730 i18n.tr("Pause") : i18n.tr("Play")
731 keywords: player.playbackState === MediaPlayer.PlayingState ?
732 i18n.tr("Pause Playback") : i18n.tr("Continue or start playback")
733- onTriggered: {
734- if (player.playbackState === MediaPlayer.PlayingState) {
735- player.pause()
736- } else {
737- player.play()
738- }
739- }
740+ onTriggered: player.toggle()
741 }
742 Action {
743 id: backAction
744@@ -112,7 +106,7 @@
745 id: prevAction
746 text: i18n.tr("Previous")
747 keywords: i18n.tr("Previous Track")
748- onTriggered: previousSong()
749+ onTriggered: player.previousSong()
750 }
751 Action {
752 id: stopAction
753@@ -190,6 +184,36 @@
754 domain: "com.ubuntu.music"
755 }
756
757+ // Connections for usermetrics
758+ Connections {
759+ id: userMetricPlayerConnection
760+ target: player
761+ property bool songCounted: false
762+
763+ onSourceChanged: {
764+ songCounted = false
765+ }
766+
767+ onPositionChanged: {
768+ // Increment song count on Welcome screen if song has been
769+ // playing for over 10 seconds.
770+ if (player.position > 10000 && !songCounted) {
771+ songCounted = true
772+ songsMetric.increment()
773+ console.debug("Increment UserMetrics")
774+ }
775+ }
776+ }
777+
778+ // Connections for powerd
779+ Connections {
780+ target: player
781+ onPlaybackStateChanged: {
782+ QtPowerd.keepAlive = player.playbackState === MediaPlayer.PlayingState
783+ console.log("QtPowerd.keepAlive=" + QtPowerd.keepAlive)
784+ }
785+ }
786+
787 // Design stuff
788 Style { id: styleMusic }
789 width: units.gu(100)
790@@ -234,7 +258,6 @@
791 Playlists.initializePlaylist()
792 // everything else
793 loading.visible = true
794- random = Settings.getSetting("shuffle") == "1" // shuffle state
795 scrobble = Settings.getSetting("scrobble") == "1" // scrobble state
796 lastfmusername = Settings.getSetting("lastfmusername") // lastfm username
797 lastfmpassword = Settings.getSetting("lastfmpassword") // lastfm password
798@@ -250,10 +273,7 @@
799 // VARIABLES
800 property string musicName: i18n.tr("Music")
801 property string appVersion: '1.2'
802- property bool isPlaying: false
803- property bool songCounted: false
804 property bool hasRecent: !Library.isRecentEmpty()
805- property bool random: false
806 property bool scrobble: false
807 property string lastfmusername
808 property string lastfmpassword
809@@ -266,26 +286,13 @@
810 property string chosenCover: ""
811 property string chosenGenre: ""
812 property int chosenIndex: 0
813- property string currentArtist: ""
814- property string currentAlbum: ""
815- property string currentTracktitle: ""
816- property string currentFile: ""
817- property int currentIndex: -1
818 property LibraryListModel currentModel: null // Current model being used
819 property var currentQuery: null
820 property var currentParam: null
821- property string currentCover: ""
822- property string currentCoverSmall: currentCover === "" ?
823- "images/cover_default_icon.png" :
824- currentCover
825- property string currentCoverFull: currentCover !== "" ?
826- currentCover :
827- "images/cover_default.png"
828 property bool queueChanged: false
829 property bool toolbarShown: musicToolbar.shown
830 signal collapseExpand(int index);
831 signal collapseSwipeDelete(int index);
832- signal onPlayingTrackChange(string source)
833 signal onToolbarShownChanged(bool shown, var currentPage, var currentTab)
834
835 property bool wideAspect: width >= units.gu(70)
836@@ -301,105 +308,6 @@
837 }
838 }
839
840- function previousSong(startPlaying) {
841- getSong(-1, startPlaying)
842- }
843-
844-
845- function nextSong(startPlaying, fromControls) {
846- getSong(1, startPlaying, fromControls)
847- }
848-
849- function stopSong() {
850- currentIndex = -1;
851- player.source = ""; // changing to "" triggers the player to stop and removes the highlight
852- }
853-
854- function getSong(direction, startPlaying, fromControls) {
855-
856- // Reset the songCounted property to false since this is a new track
857- songCounted = false
858-
859- // Seek to start if threshold reached when selecting previous
860- if (direction === -1 && (player.position / 1000) > 5)
861- {
862- player.seek(0); // seek to start
863- return;
864- }
865-
866- if (trackQueue.model.count == 0)
867- {
868- customdebug("No tracks in queue.");
869- return;
870- }
871-
872- if (startPlaying === undefined) // default startPlaying to true
873- {
874- startPlaying = true;
875- }
876-
877- if (fromControls === undefined) // default fromControls to true
878- {
879- fromControls = true;
880- }
881-
882- if (random) {
883- var now = new Date();
884- var seed = now.getSeconds();
885-
886- // trackQueue must be above 1 otherwise an infinite loop will occur
887- do {
888- var num = (Math.floor((trackQueue.model.count) * Math.random(seed)));
889- console.log(num)
890- } while (num == currentIndex && trackQueue.model.count > 1)
891- currentIndex = num
892- player.source = Qt.resolvedUrl(trackQueue.model.get(num).file)
893- console.log("MediaPlayer statusChanged, currentIndex: " + currentIndex)
894- } else {
895- if ((currentIndex < trackQueue.model.count - 1 && direction === 1 )
896- || (currentIndex > 0 && direction === -1)) {
897- console.log("currentIndex: " + currentIndex)
898- console.log("trackQueue.count: " + trackQueue.model.count)
899-
900- currentIndex += direction
901- player.source = Qt.resolvedUrl(trackQueue.model.get(currentIndex).file)
902- } else if(direction === 1 && (Settings.getSetting("repeat") === "1" || fromControls)) {
903- console.log("currentIndex: " + currentIndex)
904- console.log("trackQueue.count: " + trackQueue.model.count)
905- currentIndex = 0
906- player.source = Qt.resolvedUrl(trackQueue.model.get(currentIndex).file)
907- } else if(direction === -1 && (Settings.getSetting("repeat") === "1" || fromControls)) {
908- console.log("currentIndex: " + currentIndex)
909- console.log("trackQueue.count: " + trackQueue.model.count)
910- currentIndex = trackQueue.model.count - 1
911- player.source = Qt.resolvedUrl(trackQueue.model.get(currentIndex).file)
912- }
913- else
914- {
915- player.stop()
916- return;
917- }
918-
919- console.log("MediaPlayer statusChanged, currentIndex: " + currentIndex)
920- }
921- player.stop() // Add stop so that if same song is selected it restarts
922- console.log("Playing: "+player.source)
923-
924- if (startPlaying === true) // only start the track if told
925- {
926- player.play()
927- }
928-
929- timestamp = new Date().getTime(); // contains current date and time in Unix time, used to scrobble
930- // scrobble it
931- if (Settings.getSetting("scrobble") === "1") {
932- Scrobble.now_playing(player.source,timestamp) // send "now playing" to last.fm
933- }
934- else {
935- console.debug("Debug: no scrobbling")
936- }
937- }
938-
939 // Add items from a stored query in libraryModel into the queue
940 function addQueueFromModel(libraryModel)
941 {
942@@ -425,22 +333,23 @@
943 }
944 }
945
946+ // Converts an duration in ms to a formated string ("minutes:seconds")
947+ function durationToString(duration) {
948+ var minutes = Math.floor((duration/1000) / 60);
949+ var seconds = Math.floor((duration/1000)) % 60;
950+ return minutes + ":" + (seconds<10 ? "0"+seconds : seconds);
951+ }
952+
953 function trackClicked(libraryModel, index, play)
954 {
955- if (play === undefined)
956- {
957- play = true
958- }
959+ play = play === undefined ? true : play // default play to true
960
961- if (index > libraryModel.model.count - 1 || index < 0)
962- {
963+ if (index > libraryModel.model.count - 1 || index < 0) {
964 customdebug("Incorrect index given to trackClicked.")
965 return;
966 }
967
968- var file = libraryModel.model.get(index).file
969-
970- console.debug(player.source, Qt.resolvedUrl(file))
971+ var file = Qt.resolvedUrl(libraryModel.model.get(index).file)
972
973 // Clear the play queue and load the new tracks - if not trackQueue
974 // Don't reload queue if model, query and parameters are the same
975@@ -454,31 +363,16 @@
976 trackQueue.model.clear()
977 addQueueFromModel(libraryModel)
978 }
979- else if (player.source == Qt.resolvedUrl(file) &&
980- currentIndex === index)
981+ else if (player.source == file &&
982+ player.currentIndex === index)
983 {
984- if (play === false)
985- {
986- return
987- }
988-
989- console.log("Is current track: "+player.playbackState)
990-
991- if (player.playbackState == MediaPlayer.PlayingState)
992- {
993+ // Same track so just toggle playing state
994+ if (play === true) {
995+ console.log("Is current track: "+player.playbackState)
996+
997 if (musicToolbar.currentPage == nowPlaying) {
998- player.pause()
999- } else { // We are not on the now playing page
1000- // Show the Now Playing page and make sure the track is visible
1001- nowPlaying.visible = true;
1002- nowPlaying.ensureVisibleIndex = index;
1003-
1004- musicToolbar.showToolbar();
1005+ player.toggle()
1006 }
1007- }
1008- else
1009- {
1010- player.play()
1011
1012 // Show the Now Playing page and make sure the track is visible
1013 nowPlaying.visible = true;
1014@@ -490,142 +384,37 @@
1015 return
1016 }
1017
1018- // Reset the songCounted property to false since this is a new track
1019- songCounted = false
1020-
1021 // Current index must be updated before player.source
1022 currentModel = libraryModel
1023 currentQuery = libraryModel.query
1024 currentParam = libraryModel.param
1025- currentIndex = trackQueue.model.get(index).file === file ?
1026- index : trackQueue.indexOf(file) // pick given index first
1027+
1028+ if (Qt.resolvedUrl(trackQueue.model.get(index).file) != file) {
1029+ index = trackQueue.indexOf(file) // pick given index first
1030+ }
1031 queueChanged = false
1032
1033 console.log("Click of fileName: " + file)
1034
1035- if (play === true)
1036- {
1037- player.stop()
1038- }
1039-
1040- if (player.source == Qt.resolvedUrl(file)) {
1041- onPlayingTrackChange(player.source)
1042- }
1043-
1044- player.source = Qt.resolvedUrl(file)
1045-
1046- if (play === true)
1047- {
1048- player.play()
1049- }
1050-
1051- console.log("Source: " + player.source.toString())
1052- console.log("Index: " + currentIndex)
1053-
1054- if (play === true)
1055- {
1056+ if (play === true) {
1057+ player.playSong(file, index)
1058+
1059 // Show the Now Playing page and make sure the track is visible
1060 nowPlaying.visible = true;
1061 nowPlaying.ensureVisibleIndex = index;
1062
1063 musicToolbar.showToolbar();
1064 }
1065+ else {
1066+ player.source = file
1067+ }
1068
1069 return file
1070 }
1071
1072- function updateMeta()
1073- {
1074- // Load metadata for the track
1075- currentArtist = trackQueue.model.get(currentIndex).artist
1076- currentAlbum = trackQueue.model.get(currentIndex).album
1077- currentTracktitle = trackQueue.model.get(currentIndex).title
1078-
1079- // hasCover and currentCover require no file://
1080- var file = currentFile
1081-
1082- if (file.indexOf("file://") == 0)
1083- {
1084- file = file.slice(7, file.length)
1085- }
1086-
1087- currentCover = trackQueue.model.get(currentIndex).cover !== "" ? trackQueue.model.get(currentIndex).cover : "images/cover_default_icon.png"
1088- }
1089-
1090 // WHERE THE MAGIC HAPPENS
1091- MediaPlayer {
1092+ Player {
1093 id: player
1094- objectName: "player"
1095- muted: false
1096-
1097- signal positionChange(int position, int duration)
1098-
1099- property bool seeking: false; // Is the user seeking?
1100-
1101- // String versions of pos/dur that labels listen to
1102- property string durationStr: "00:00"
1103- property string positionStr: "00:00"
1104-
1105- onSourceChanged: {
1106- currentFile = source
1107-
1108- if (source != "" && source != undefined && source !== false)
1109- {
1110- onPlayingTrackChange(source)
1111- updateMeta()
1112- }
1113- else
1114- {
1115- onPlayingTrackChange(source) // removes highlight as will get -1 index
1116- player.stop()
1117- }
1118- }
1119-
1120- onStatusChanged: {
1121- if (status == MediaPlayer.EndOfMedia) {
1122- // scrobble it
1123- if (Settings.getSetting("scrobble") === "1") {
1124- Scrobble.scrobble(player.source,currentArtist,timestamp)
1125- }
1126- else {
1127- console.debug("Debug: no scrobbling")
1128- }
1129-
1130- nextSong (true, false) // next track
1131- }
1132- }
1133-
1134- // Update the duration text unless seeking (seeking overrides the text)
1135- onDurationChanged: {
1136- if (seeking == false)
1137- {
1138- durationStr = __durationToString(player.duration)
1139- }
1140-
1141- positionChange(position, duration)
1142- }
1143-
1144- // Update the position text unless seeking (seeking overrides the text)
1145- onPositionChanged: {
1146- if (seeking == false)
1147- {
1148- positionStr = __durationToString(player.position)
1149- }
1150-
1151- // Increment song count on Welcome screen if song has been playing for over 10 seconds.
1152- if (player.position > 10000 && !songCounted) {
1153- songCounted = true
1154- songsMetric.increment()
1155- }
1156-
1157- positionChange(position, duration)
1158- }
1159-
1160- onPlaybackStateChanged: {
1161- mainView.isPlaying = player.playbackState === MediaPlayer.PlayingState
1162- QtPowerd.keepAlive = mainView.isPlaying
1163- console.log("mainView.isPlaying=" + mainView.isPlaying + ", QtPowerd.keepAlive=" + QtPowerd.keepAlive)
1164- }
1165 }
1166
1167 SongsSheet {
1168@@ -933,21 +722,6 @@
1169 // list of tracks on startup. This is just during development
1170 LibraryListModel {
1171 id: trackQueue
1172- property bool isEmpty: count == 0
1173-
1174- onIsEmptyChanged: {
1175- /*
1176- * If changed to false then must have been empty before
1177- * Therefore set the first song as the current item
1178- * and update any metadata
1179- */
1180- if (isEmpty === false && currentIndex == -1 && player.source == "")
1181- {
1182- currentIndex = 0;
1183- player.source = trackQueue.model.get(currentIndex).file;
1184- updateMeta();
1185- }
1186- }
1187 }
1188
1189 // list of songs, which has been removed.
1190@@ -1294,11 +1068,4 @@
1191 id: addtoPlaylist
1192 }
1193
1194- // Converts an duration in ms to a formated string ("minutes:seconds")
1195- function __durationToString(duration) {
1196- var minutes = Math.floor((duration/1000) / 60);
1197- var seconds = Math.floor((duration/1000)) % 60;
1198- return minutes + ":" + (seconds<10 ? "0"+seconds : seconds);
1199- }
1200-
1201 } // end of main view
1202
1203=== modified file 'tests/autopilot/music_app/emulators.py'
1204--- tests/autopilot/music_app/emulators.py 2014-01-17 22:24:22 +0000
1205+++ tests/autopilot/music_app/emulators.py 2014-03-07 16:40:44 +0000
1206@@ -51,6 +51,9 @@
1207
1208 self.pointing_device.drag(x1, y1, x1, y1 - toolbar.height)
1209
1210+ def get_player(self):
1211+ return self.select_single("*", objectName="player")
1212+
1213 def get_play_button(self):
1214 return self.wait_select_single("*", objectName="playshape")
1215
1216
1217=== modified file 'tests/autopilot/music_app/tests/__init__.py'
1218--- tests/autopilot/music_app/tests/__init__.py 2014-01-31 20:43:59 +0000
1219+++ tests/autopilot/music_app/tests/__init__.py 2014-03-07 16:40:44 +0000
1220@@ -242,5 +242,9 @@
1221 logger.error("Failed to restore database")
1222
1223 @property
1224+ def player(self):
1225+ return self.main_view.get_player()
1226+
1227+ @property
1228 def main_view(self):
1229 return self.app.select_single(emulators.MainView)
1230
1231=== modified file 'tests/autopilot/music_app/tests/test_music.py'
1232--- tests/autopilot/music_app/tests/test_music.py 2014-03-06 17:23:56 +0000
1233+++ tests/autopilot/music_app/tests/test_music.py 2014-03-07 16:40:44 +0000
1234@@ -41,8 +41,8 @@
1235 song = self.main_view.get_album_sheet_listview_tracktitle(trackTitle)
1236 self.pointing_device.click_object(song)
1237
1238- title = lambda: self.main_view.currentTracktitle
1239- artist = lambda: self.main_view.currentArtist
1240+ title = lambda: self.player.currentMetaTitle
1241+ artist = lambda: self.player.currentMetaArtist
1242 self.assertThat(title,
1243 Eventually(Equals("Foss Yeaaaah! (Radio Edit)")))
1244 self.assertThat(artist, Eventually(Equals("Benjamin Kerensa")))
1245@@ -69,15 +69,15 @@
1246 playbutton = self.main_view.get_play_button()
1247
1248 """ Track is playing"""
1249- self.assertThat(self.main_view.isPlaying, Eventually(Equals(True)))
1250+ self.assertThat(self.player.isPlaying, Eventually(Equals(True)))
1251 self.pointing_device.click_object(playbutton)
1252
1253 """ Track is not playing"""
1254- self.assertThat(self.main_view.isPlaying, Eventually(Equals(False)))
1255+ self.assertThat(self.player.isPlaying, Eventually(Equals(False)))
1256
1257 """ Track is playing"""
1258 self.pointing_device.click_object(playbutton)
1259- self.assertThat(self.main_view.isPlaying, Eventually(Equals(True)))
1260+ self.assertThat(self.player.isPlaying, Eventually(Equals(True)))
1261
1262 def test_play_pause_now_playing(self):
1263 """ Test playing and pausing a track (Music Library must exist) """
1264@@ -92,15 +92,15 @@
1265 playbutton = self.main_view.get_now_playing_play_button()
1266
1267 """ Track is playing"""
1268- self.assertThat(self.main_view.isPlaying, Eventually(Equals(True)))
1269+ self.assertThat(self.player.isPlaying, Eventually(Equals(True)))
1270 self.pointing_device.click_object(playbutton)
1271
1272 """ Track is not playing"""
1273- self.assertThat(self.main_view.isPlaying, Eventually(Equals(False)))
1274+ self.assertThat(self.player.isPlaying, Eventually(Equals(False)))
1275
1276 """ Track is playing"""
1277 self.pointing_device.click_object(playbutton)
1278- self.assertThat(self.main_view.isPlaying, Eventually(Equals(True)))
1279+ self.assertThat(self.player.isPlaying, Eventually(Equals(True)))
1280
1281 def test_next_previous(self):
1282 """ Test going to next track (Music Library must exist) """
1283@@ -115,49 +115,49 @@
1284 playbutton = self.main_view.get_now_playing_play_button()
1285 shufflebutton = self.main_view.get_shuffle_button()
1286
1287- title = lambda: self.main_view.currentTracktitle
1288- artist = lambda: self.main_view.currentArtist
1289+ title = lambda: self.player.currentMetaTitle
1290+ artist = lambda: self.player.currentMetaArtist
1291
1292- orgTitle = self.main_view.currentTracktitle
1293- orgArtist = self.main_view.currentArtist
1294+ orgTitle = self.player.currentMetaTitle
1295+ orgArtist = self.player.currentMetaArtist
1296
1297 #check original track
1298- self.assertThat(self.main_view.isPlaying, Eventually(Equals(True)))
1299+ self.assertThat(self.player.isPlaying, Eventually(Equals(True)))
1300 logger.debug("Original Song %s, %s" % (orgTitle, orgArtist))
1301
1302 """ Pause track """
1303 self.pointing_device.click_object(playbutton)
1304- self.assertThat(self.main_view.isPlaying, Eventually(Equals(False)))
1305+ self.assertThat(self.player.isPlaying, Eventually(Equals(False)))
1306
1307 #ensure shuffle is off
1308- if self.main_view.random:
1309+ if self.player.shuffle:
1310 logger.debug("Turning off shuffle")
1311 self.pointing_device.click_object(shufflebutton)
1312 else:
1313 logger.debug("Shuffle already off")
1314- self.assertThat(self.main_view.random, Eventually(Equals(False)))
1315+ self.assertThat(self.player.shuffle, Eventually(Equals(False)))
1316
1317 """ Select next """
1318 #goal is to go back and forth and ensure 2 different songs
1319 forwardbutton = self.main_view.get_forward_button()
1320 self.pointing_device.click_object(forwardbutton)
1321- self.assertThat(self.main_view.isPlaying, Eventually(Equals(True)))
1322+ self.assertThat(self.player.isPlaying, Eventually(Equals(True)))
1323
1324 #ensure different song
1325 self.assertThat(title, Eventually(NotEquals(orgTitle)))
1326 self.assertThat(artist, Eventually(NotEquals(orgArtist)))
1327- nextTitle = self.main_view.currentTracktitle
1328- nextArtist = self.main_view.currentArtist
1329+ nextTitle = self.player.currentMetaTitle
1330+ nextArtist = self.player.currentMetaArtist
1331 logger.debug("Next Song %s, %s" % (nextTitle, nextArtist))
1332
1333 """ Pause track """
1334 self.pointing_device.click_object(playbutton)
1335- self.assertThat(self.main_view.isPlaying, Eventually(Equals(False)))
1336+ self.assertThat(self.player.isPlaying, Eventually(Equals(False)))
1337
1338 """ Select previous """
1339 previousbutton = self.main_view.get_previous_button()
1340 self.pointing_device.click_object(previousbutton)
1341- self.assertThat(self.main_view.isPlaying, Eventually(Equals(True)))
1342+ self.assertThat(self.player.isPlaying, Eventually(Equals(True)))
1343
1344 #ensure we're back to original song
1345 self.assertThat(title, Eventually(Equals(orgTitle)))
1346@@ -176,19 +176,19 @@
1347 playbutton = self.main_view.get_now_playing_play_button()
1348 shufflebutton = self.main_view.get_shuffle_button()
1349
1350- title = self.main_view.currentTracktitle
1351- artist = self.main_view.currentArtist
1352+ title = self.player.currentMetaTitle
1353+ artist = self.player.currentMetaArtist
1354
1355 #ensure track is playing
1356- self.assertThat(self.main_view.isPlaying, Eventually(Equals(True)))
1357+ self.assertThat(self.player.isPlaying, Eventually(Equals(True)))
1358
1359 #ensure shuffle is off
1360- if self.main_view.random:
1361+ if self.player.shuffle:
1362 logger.debug("Turning off shuffle")
1363 self.pointing_device.click_object(shufflebutton)
1364 else:
1365 logger.debug("Shuffle already off")
1366- self.assertThat(self.main_view.random, Eventually(Equals(False)))
1367+ self.assertThat(self.player.shuffle, Eventually(Equals(False)))
1368
1369 """ Track is playing """
1370 count = 1
1371@@ -200,25 +200,25 @@
1372
1373 """ Pause track """
1374 self.pointing_device.click_object(playbutton)
1375- self.assertThat(self.main_view.isPlaying,
1376+ self.assertThat(self.player.isPlaying,
1377 Eventually(Equals(False)))
1378
1379 """ Select next """
1380 forwardbutton = self.main_view.get_forward_button()
1381 self.pointing_device.click_object(forwardbutton)
1382- self.assertThat(self.main_view.isPlaying, Eventually(Equals(True)))
1383+ self.assertThat(self.player.isPlaying, Eventually(Equals(True)))
1384
1385- title = self.main_view.currentTracktitle
1386- artist = self.main_view.currentArtist
1387+ title = self.player.currentMetaTitle
1388+ artist = self.player.currentMetaArtist
1389 logger.debug("Current Song %s, %s" % (title, artist))
1390- logger.debug("File found %s" % self.main_view.currentFile)
1391+ logger.debug("File found %s" % self.player.currentMetaFile)
1392
1393 count = count + 1
1394
1395 #make sure mp3 plays
1396- self.assertThat(self.main_view.currentFile.endswith("mp3"),
1397+ self.assertThat(self.player.source.endswith("mp3"),
1398 Equals(True))
1399- self.assertThat(self.main_view.isPlaying, Eventually(Equals(True)))
1400+ self.assertThat(self.player.isPlaying, Eventually(Equals(True)))
1401
1402 def test_shuffle(self):
1403 """ Test shuffle (Music Library must exist) """
1404@@ -237,16 +237,16 @@
1405 previousbutton = self.main_view.get_previous_button()
1406
1407 #play for a second, then pause
1408- if not self.main_view.isPlaying:
1409+ if not self.player.isPlaying:
1410 logger.debug("Play not selected")
1411 self.pointing_device.click_object(playbutton)
1412 else:
1413 logger.debug("Already playing")
1414
1415- self.assertThat(self.main_view.isPlaying, Eventually(Equals(True)))
1416+ self.assertThat(self.player.isPlaying, Eventually(Equals(True)))
1417 time.sleep(1)
1418 self.pointing_device.click_object(playbutton)
1419- self.assertThat(self.main_view.isPlaying, Eventually(Equals(False)))
1420+ self.assertThat(self.player.isPlaying, Eventually(Equals(False)))
1421
1422 count = 0
1423 while True:
1424@@ -257,47 +257,47 @@
1425 #this means a true shuffle happened
1426 #if it doesn't try again, up to count times
1427
1428- orgTitle = self.main_view.currentTracktitle
1429- orgArtist = self.main_view.currentArtist
1430+ orgTitle = self.player.currentMetaTitle
1431+ orgArtist = self.player.currentMetaArtist
1432 logger.debug("Original Song %s, %s" % (orgTitle, orgArtist))
1433
1434 if (not self.main_view.toolbarShown):
1435 self.main_view.show_toolbar()
1436
1437 #ensure shuffle is on
1438- if not self.main_view.random:
1439+ if not self.player.shuffle:
1440 logger.debug("Turning on shuffle")
1441 self.pointing_device.click_object(shufflebutton)
1442 else:
1443 logger.debug("Shuffle already on")
1444- self.assertThat(self.main_view.random, Eventually(Equals(True)))
1445+ self.assertThat(self.player.shuffle, Eventually(Equals(True)))
1446
1447 self.pointing_device.click_object(forwardbutton)
1448- self.assertThat(self.main_view.isPlaying,
1449+ self.assertThat(self.player.isPlaying,
1450 Eventually(Equals(True)))
1451- title = self.main_view.currentTracktitle
1452- artist = self.main_view.currentArtist
1453+ title = self.player.currentMetaTitle
1454+ artist = self.player.currentMetaArtist
1455 logger.debug("Current Song %s, %s" % (title, artist))
1456
1457 #go back to previous and check against original
1458 #play song, then pause before switching
1459 time.sleep(1)
1460 self.pointing_device.click_object(playbutton)
1461- self.assertThat(self.main_view.isPlaying,
1462+ self.assertThat(self.player.isPlaying,
1463 Eventually(Equals(False)))
1464
1465 #ensure shuffle is off
1466- if self.main_view.random:
1467+ if self.player.shuffle:
1468 logger.debug("Turning off shuffle")
1469 self.pointing_device.click_object(shufflebutton)
1470 else:
1471 logger.debug("Shuffle already off")
1472- self.assertThat(self.main_view.random, Eventually(Equals(False)))
1473+ self.assertThat(self.player.shuffle, Eventually(Equals(False)))
1474
1475 self.pointing_device.click_object(previousbutton)
1476
1477- title = self.main_view.currentTracktitle
1478- artist = self.main_view.currentArtist
1479+ title = self.player.currentMetaTitle
1480+ artist = self.player.currentMetaArtist
1481
1482 if title != orgTitle and artist != orgArtist:
1483 #we shuffled properly
1484@@ -362,9 +362,9 @@
1485 self.assertThat(endtracksCount, Equals(initialtracksCount + 1))
1486
1487 #Assert that the song added to the list is not playing
1488- self.assertThat(self.main_view.currentIndex,
1489+ self.assertThat(self.player.currentIndex,
1490 Eventually(NotEquals(endtracksCount)))
1491- self.assertThat(self.main_view.isPlaying, Eventually(Equals(False)))
1492+ self.assertThat(self.player.isPlaying, Eventually(Equals(False)))
1493
1494 #verify song's metadata matches the item added to the Now Playing view
1495 queueArtistName = self.main_view.get_queue_now_playing_artist(
1496@@ -401,9 +401,9 @@
1497 self.assertThat(endtracksCount, Equals(initialtracksCount + 3))
1498
1499 # Assert that the song added to the list is playing
1500- self.assertThat(self.main_view.currentIndex,
1501+ self.assertThat(self.player.currentIndex,
1502 Eventually(NotEquals(endtracksCount)))
1503- self.assertThat(self.main_view.isPlaying, Eventually(Equals(True)))
1504+ self.assertThat(self.player.isPlaying, Eventually(Equals(True)))
1505
1506 # verify song's metadata matches the item added to the Now Playing view
1507 queueArtistName = self.main_view.get_queue_now_playing_artist(
1508@@ -437,9 +437,9 @@
1509 self.assertThat(endtracksCount, Equals(initialtracksCount + 1))
1510
1511 # Assert that the song added to the list is not playing
1512- self.assertThat(self.main_view.currentIndex,
1513+ self.assertThat(self.player.currentIndex,
1514 Eventually(NotEquals(endtracksCount)))
1515- self.assertThat(self.main_view.isPlaying, Eventually(Equals(False)))
1516+ self.assertThat(self.player.isPlaying, Eventually(Equals(False)))
1517
1518 # verify song's metadata matches the item added to the Now Playing view
1519 queueArtistName = self.main_view.get_queue_now_playing_artist(
1520@@ -534,9 +534,9 @@
1521 self.assertThat(endtracksCount, Equals(initialtracksCount + 2))
1522
1523 # Assert that the song added to the list is playing
1524- self.assertThat(self.main_view.currentIndex,
1525+ self.assertThat(self.player.currentIndex,
1526 Eventually(NotEquals(endtracksCount)))
1527- self.assertThat(self.main_view.isPlaying, Eventually(Equals(True)))
1528+ self.assertThat(self.player.isPlaying, Eventually(Equals(True)))
1529
1530 # verify song's metadata matches the item added to the Now Playing view
1531 queueArtistName = self.main_view.get_queue_now_playing_artist(

Subscribers

People subscribed via source and target branches

to status/vote changes: