Merge lp:~mzanetti/unity8/music-preview into lp:unity8
- music-preview
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Michał Sawicz |
Approved revision: | 523 |
Merged at revision: | 558 |
Proposed branch: | lp:~mzanetti/unity8/music-preview |
Merge into: | lp:unity8 |
Prerequisite: | lp:~unity-team/unity8/switching-previews |
Diff against target: |
784 lines (+661/-0) 15 files modified
Dash/DashPreview.qml (+1/-0) Dash/GenericScopeView.qml (+1/-0) Dash/Music/MusicPreview.qml (+203/-0) Dash/PreviewDelegateMapper.qml (+1/-0) debian/control (+1/-0) tests/mocks/CMakeLists.txt (+1/-0) tests/mocks/QtMultimedia/CMakeLists.txt (+19/-0) tests/mocks/QtMultimedia/audio.cpp (+107/-0) tests/mocks/QtMultimedia/audio.h (+78/-0) tests/mocks/QtMultimedia/plugin.cpp (+28/-0) tests/mocks/QtMultimedia/plugin.h (+34/-0) tests/mocks/QtMultimedia/qmldir (+2/-0) tests/qmltests/CMakeLists.txt (+1/-0) tests/qmltests/Dash/Music/tst_MusicPreview.qml (+174/-0) tests/qmltests/Dash/tst_GenericScopeView.qml (+10/-0) |
To merge this branch: | bzr merge lp:~mzanetti/unity8/music-preview |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot (community) | continuous-integration | Approve | |
Michał Sawicz | Approve | ||
Michal Hruby (community) | Needs Fixing | ||
Review via email: mp+193803@code.launchpad.net |
Commit message
Added music preview
Description of the change
Michael Zanetti (mzanetti) wrote : | # |
> Looking good overall, visually there's one separator missing between the
> orange button and the tracks, other than that I was able to to get lots of:
>
> Error: "GStreamer encountered a general stream error."
>
> ...if I swiped slowly between two albums and returned to the first after
> revealing the second. Then playing tracks no longer worked, and the one that
> was playing didn't stop when closing the preview nor when swiping to different
> previews.
>
> 168 + onPositionChanged: print("got percent",
> percent, position, duration)
>
> Remove pls...
>
> 233 +MusicPreviewTr
>
> MusicPreviewTra
>
> And as last thing, some ui tests would be nice? :)
Yes. tests are coming, that's why it is WIP still. Will fix rest of the stuff when I'm back next week.
Michael Zanetti (mzanetti) wrote : | # |
> Looking good overall, visually there's one separator missing between the
> orange button and the tracks,
fixed.
> other than that I was able to to get lots of:
>
> Error: "GStreamer encountered a general stream error."
>
> ...if I swiped slowly between two albums and returned to the first after
> revealing the second. Then playing tracks no longer worked, and the one that
> was playing didn't stop when closing the preview nor when swiping to different
> previews.
This should be fixed. Switching to another preview stops the playback on the current one. I still see the GStreamer warning every once in a while, but it doesn't seem to break anything.
>
> 168 + onPositionChanged: print("got percent",
> percent, position, duration)
>
> Remove pls...
removed.
>
> 233 +MusicPreviewTr
>
> MusicPreviewTra
fixed.
>
> And as last thing, some ui tests would be nice? :)
added.
Michał Sawicz (saviq) wrote : | # |
Hmmm this is playing music in-process in the shell... I'm ok-ish with that if we make sure that's a temporary thing. We should make sure to use the media service when it's available.
Michael Zanetti (mzanetti) wrote : | # |
> Hmmm this is playing music in-process in the shell... I'm ok-ish with that if
> we make sure that's a temporary thing. We should make sure to use the media
> service when it's available.
IMO the Audio {} element should use the media player service implicitly. Otherwise we would have to forbid people to use the Audio element at all and provide our own one instead. Something I strongly object to.
Michal Hruby (mhr3) wrote : | # |
> > Error: "GStreamer encountered a general stream error."
> >
> > ...if I swiped slowly between two albums and returned to the first after
> > revealing the second. Then playing tracks no longer worked, and the one that
> > was playing didn't stop when closing the preview nor when swiping to
> different
> > previews.
>
> This should be fixed. Switching to another preview stops the playback on the
> current one. I still see the GStreamer warning every once in a while, but it
> doesn't seem to break anything.
I still see those errors and after a while playback stops working, but it doesn't break as soon as you start seeing them. Just keep swiping back and forth as described.
Michał Sawicz (saviq) wrote : | # |
On 12.11.2013 14:26, Michael Zanetti wrote:
> IMO the Audio {} element should use the media player service implicitly. Otherwise we would have to forbid people to use the Audio element at all and provide our own one instead. Something I strongly object to.
Right. Result will be the same (we'll be using the system-wide
media-player), even if this code will not change at all ;).
I'm game with that, then.
Michael Zanetti (mzanetti) wrote : | # |
> I still see those errors and after a while playback stops working, but it
> doesn't break as soon as you start seeing them. Just keep swiping back and
> forth as described.
After debugging this for a while this seems to be a race condition somewhere in the deeper layers. It happens when there are too many Audio elements instantiated and deleted simultaneously. I changed the code to only use one Audio element per preview which is a better idea in any case. Should also prevent pulseaudio from locking up.
Michał Sawicz (saviq) wrote : | # |
Missing Dash/Music/
Michael Zanetti (mzanetti) wrote : | # |
> Missing Dash/Music/
fixed
Michał Sawicz (saviq) wrote : | # |
8 + property bool isCurrent: false
20 + Binding {
21 + target: item
22 + property: "isCurrent"
23 + value: previewListView
24 + }
25 +
102 + Connections {
103 + target: root
104 + onIsCurrentChanged: {
105 + if (!root.isCurrent) {
106 + audioPlayer.stop();
107 + }
108 + }
109 + }
ListView.
=====
99 + onErrorStringCh
Use console.warn() please, so that we can shut this up when we implement a log printer.
=====
101 + }
102 + Connections {
176 + }
177 + MouseArea {
Newline please. There's a few other places, too.
====
112 + anchors {
113 + left: parent.left
114 + right: parent.right
115 + }
121 + anchors {
122 + left: parent.left
123 + right: parent.right
124 + }
Compress to one line?
=====
117 + height: childrenRect.height
That's the default for a Column.
=====
125 + visible: trackRepeater.count > 0
Shouldn't the whole Column be made invisible?
====
139 + property bool isPlayingItem: false
143 + onSourceChanged: trackItem.
151 + isPlayingItem = true;
I'd be worried this might get out of sync... I know here it's pretty reliable, but just consider this: I usually add a "property Item playingItem" to the Repeater/*View, and then do "isPlayingItem: repeater.
=====
157 + width: parent.width
162 + anchors.
Use anchors for width, too, please?
=====
164 + UbuntuShape {
165 + id: playButtonShape
Why not Button/
=====
159 + property int column1Width: units.gu(3)
160 + property int column2Width: width - (2 * spacing) - column1Width - column3Width
161 + property int column3Width: units.gu(4)
Eek... /me wants Qt 5.2's layouts ;)
=====
206 + UbuntuShape {
207 + id: progressBarFill
No way to use the SDK's progress bar that small? Please chat with them whether they'd want it there.
=====
223 + top: parent.bottom
I'm always frowning at items outside of their parents...
=====
229 +
Bad newline.
=====
We sure QtMultimedia will work in all our testing environments? Should we maybe think of a mock QtMultimedia backend?
=====
Missing Depends on qtmultimedia.
Michael Zanetti (mzanetti) wrote : | # |
> ListView.
fixed.
>
> =====
>
> 99 + onErrorStringCh
>
> Use console.warn() please, so that we can shut this up when we implement a log
> printer.
fixed.
>
> =====
> Newline please. There's a few other places, too.
fixed.
>
> ====
>
> Compress to one line?
fixed
>
> =====
>
> 117 + height: childrenRect.height
>
> That's the default for a Column.
I don't trust it :D ... removed.
>
> =====
>
> 125 + visible: trackRepeater.count > 0
>
> Shouldn't the whole Column be made invisible?
Doesn't really make a difference I'd say. but sure. might spare us some very few cpu cycles indeed. moved
>
> ====
>
> 139 + property bool isPlayingItem: false
>
> 143 + onSourceChanged: trackItem.
>
> 151 + isPlayingItem = true;
>
> I'd be worried this might get out of sync... I know here it's pretty reliable,
> but just consider this: I usually add a "property Item playingItem" to the
> Repeater/*View, and then do "isPlayingItem: repeater.
> what do you think?
Good point, yes. fixed.
>
> =====
>
> Use anchors for width, too, please?
fixed.
>
> =====
>
> 164 + UbuntuShape {
> 165 + id: playButtonShape
>
> Why not Button/
hmm... at least so far it only plays when clicking the play button. Need to check with design. But isn't the touchdown effect exactly what we did *not* want to have according to some bug for the people lens?
>
> =====
>
> 159 + property int column1Width: units.gu(3)
> 160 + property int column2Width: width - (2 * spacing) - column1Width -
> column3Width
> 161 + property int column3Width: units.gu(4)
>
> Eek... /me wants Qt 5.2's layouts ;)
+1 :)
>
> ========
>
> 223 + top: parent.bottom
>
> I'm always frowning at items outside of their parents...
I agree it's usually bad practice. In this small case however it greatly simplifies layouting as the text should still be vCentered and the progress bar attached to it. Also no contained items or clipping items around. Ok with exception?
>
> =====
>
> 229 +
>
> Bad newline.
fixed
>
>
> Missing Depends on qtmultimedia.
fixed.
*****************
Still to clarify:
> =====
>
> We sure QtMultimedia will work in all our testing environments? Should we
> maybe think of a mock QtMultimedia backend?
>
> =====
>
> 206 + UbuntuShape {
> 207 + id: progressBarFill
>
> No way to use the SDK's progress bar that small? Please chat with them whether
> they'd want it there.
>
> =====
Michael Zanetti (mzanetti) wrote : | # |
>
> 206 + UbuntuShape {
> 207 + id: progressBarFill
>
> No way to use the SDK's progress bar that small? Please chat with them whether
> they'd want it there.
Evaluated this again and had a chat with SDK: Cannot use the SDK's progress bar as it doesn't really fit design wise and implementation wise. Given that this will need to have the cached percentage indication too at some point (when scopes backend supports it) SDK guys think this is too specific to Music players to be included in the SDK.
> ====
>
> We sure QtMultimedia will work in all our testing environments? Should we
> maybe think of a mock QtMultimedia backend?
>
mocked.
Michał Sawicz (saviq) wrote : | # |
On 13.11.2013 20:12, Michael Zanetti wrote:
> hmm... at least so far it only plays when clicking the play button. Need to check with design. But isn't the touchdown effect exactly what we did *not* want to have according to some bug for the people lens?
I meant why don't you use AbstractButton/
itself - not for the whole delegate. Also, we didn't want a touchdown
effect kick in too soon when swiping, we want it when you actually
press. Sure, this button is smallish, so not visible with a finger, but
it will be visible with a mouse, so let's add it.
>> I'm always frowning at items outside of their parents...
>
> I agree it's usually bad practice. In this small case however it greatly simplifies layouting as the text should still be vCentered and the progress bar attached to it. Also no contained items or clipping items around. Ok with exception?
Yeah, OK.
> Evaluated this again and had a chat with SDK: Cannot use the SDK's progress bar as it doesn't really fit design wise and implementation wise. Given that this will need to have the cached percentage indication too at some point (when scopes backend supports it) SDK guys think this is too specific to Music players to be included in the SDK.
OK.
>> We sure QtMultimedia will work in all our testing environments? Should we
>> maybe think of a mock QtMultimedia backend?
>
> mocked.
Awesome.
--
Michał (Saviq) Sawicz <email address hidden>
Canonical Services Ltd.
Michael Zanetti (mzanetti) wrote : | # |
> On 13.11.2013 20:12, Michael Zanetti wrote:
>
> > hmm... at least so far it only plays when clicking the play button. Need to
> check with design. But isn't the touchdown effect exactly what we did *not*
> want to have according to some bug for the people lens?
>
> I meant why don't you use AbstractButton/
> itself - not for the whole delegate. Also, we didn't want a touchdown
> effect kick in too soon when swiping, we want it when you actually
> press. Sure, this button is smallish, so not visible with a finger, but
> it will be visible with a mouse, so let's add it.
Hmpf. Didn't work out as well as expected: The normal button is orange which is not what we want here. The AbstractButton instead doesn't have the touch down effect neither and still requires me to use the UbuntuShape as it doesn't render the icon (even though there is an iconSource property). Still I made it an AbstractButton so we'll get updates on the behaviour too.
On another note, I reverted rev 505 as in this case ListView.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:517
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:517
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:518
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:518
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Michał Sawicz (saviq) wrote : | # |
Text conflict in plugins/
Michael Zanetti (mzanetti) wrote : | # |
> Text conflict in plugins/
merged
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:519
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:519
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Michał Sawicz (saviq) wrote : | # |
It's not looking great on tablet, think we could improve it quickly?
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:520
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Michael Zanetti (mzanetti) wrote : | # |
> It's not looking great on tablet, think we could improve it quickly?
Hmm... good question. I guess the only thing would be to move the tracks list over to the ratings&comments section. However, that wouldn't match with design. But otoh I'm not sure we'll ever get ratings&comments for local music.
Michał Sawicz (saviq) wrote : | # |
OK well, we'll revisit later.
/me likes!
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:521
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:522
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:522
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:523
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
ABORTED: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
FAILURE: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
ABORTED: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
FAILURE: http://
SUCCESS: http://
Michał Sawicz (saviq) wrote : | # |
Aborted due to bug #1255578.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
PS Jenkins bot (ps-jenkins) : | # |
Preview Diff
1 | === modified file 'Dash/DashPreview.qml' |
2 | --- Dash/DashPreview.qml 2013-10-21 11:51:14 +0000 |
3 | +++ Dash/DashPreview.qml 2013-11-27 11:27:38 +0000 |
4 | @@ -25,6 +25,7 @@ |
5 | property alias showProcessingAction: waitingForActionMouseArea.enabled |
6 | |
7 | property real previewWidthRatio: 0.5 |
8 | + property bool isCurrent: false |
9 | |
10 | property Component previewImages |
11 | property Component header |
12 | |
13 | === modified file 'Dash/GenericScopeView.qml' |
14 | --- Dash/GenericScopeView.qml 2013-11-19 14:33:40 +0000 |
15 | +++ Dash/GenericScopeView.qml 2013-11-27 11:27:38 +0000 |
16 | @@ -527,6 +527,7 @@ |
17 | onLoaded: { |
18 | if (previewListView.onScreen && previewData !== undefined) { |
19 | item.previewData = Qt.binding(function() { return previewData }) |
20 | + item.isCurrent = Qt.binding(function() { return ListView.isCurrentItem }) |
21 | } |
22 | } |
23 | |
24 | |
25 | === added file 'Dash/Music/MusicPreview.qml' |
26 | --- Dash/Music/MusicPreview.qml 1970-01-01 00:00:00 +0000 |
27 | +++ Dash/Music/MusicPreview.qml 2013-11-27 11:27:38 +0000 |
28 | @@ -0,0 +1,203 @@ |
29 | +/* |
30 | + * Copyright (C) 2013 Canonical, Ltd. |
31 | + * |
32 | + * This program is free software; you can redistribute it and/or modify |
33 | + * it under the terms of the GNU General Public License as published by |
34 | + * the Free Software Foundation; version 3. |
35 | + * |
36 | + * This program is distributed in the hope that it will be useful, |
37 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
38 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
39 | + * GNU General Public License for more details. |
40 | + * |
41 | + * You should have received a copy of the GNU General Public License |
42 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
43 | + */ |
44 | + |
45 | +import QtQuick 2.0 |
46 | +import QtMultimedia 5.0 |
47 | +import Ubuntu.Components 0.1 |
48 | +import Ubuntu.Components.ListItems 0.1 |
49 | +import ".." |
50 | +import "../Generic" |
51 | +import "../../Components" |
52 | +import "../Previews" |
53 | + |
54 | +GenericPreview { |
55 | + id: root |
56 | + |
57 | + previewImages: previewImageComponent |
58 | + header: headerComponent |
59 | + description: descriptionComponent |
60 | + |
61 | + Component { |
62 | + id: previewImageComponent |
63 | + LazyImage { |
64 | + height: units.gu(22) |
65 | + scaleTo: "height" |
66 | + source: previewData ? previewData.image : "" |
67 | + initialHeight: height |
68 | + initialWidth: height |
69 | + } |
70 | + } |
71 | + |
72 | + Component { |
73 | + id: headerComponent |
74 | + Header { |
75 | + title: previewData.title |
76 | + subtitle: previewData.subtitle |
77 | + } |
78 | + } |
79 | + |
80 | + Component { |
81 | + id: descriptionComponent |
82 | + |
83 | + Item { |
84 | + height: childrenRect.height |
85 | + Audio { |
86 | + id: audioPlayer |
87 | + objectName: "audioPlayer" |
88 | + property real progress: audioPlayer.position / audioPlayer.duration |
89 | + |
90 | + property Item playingItem |
91 | + |
92 | + Component.onDestruction: { |
93 | + audioPlayer.stop(); |
94 | + } |
95 | + |
96 | + onErrorStringChanged: console.warn("Audio player error:", errorString) |
97 | + |
98 | + } |
99 | + |
100 | + Connections { |
101 | + target: root |
102 | + onIsCurrentChanged: { |
103 | + if (!root.isCurrent) { |
104 | + audioPlayer.stop(); |
105 | + } |
106 | + } |
107 | + } |
108 | + |
109 | + Column { |
110 | + anchors { left: parent.left; right: parent.right } |
111 | + visible: trackRepeater.count > 0 |
112 | + |
113 | + ThinDivider { |
114 | + objectName: "topDivider" |
115 | + anchors { left: parent.left; right: parent.right } |
116 | + } |
117 | + |
118 | + Repeater { |
119 | + id: trackRepeater |
120 | + objectName: "trackRepeater" |
121 | + |
122 | + model: previewData.tracks |
123 | + |
124 | + delegate: Item { |
125 | + id: trackItem |
126 | + objectName: "trackItem" + index |
127 | + anchors { left: parent.left; right: parent.right } |
128 | + height: units.gu(5) |
129 | + property bool isPlayingItem: audioPlayer.playingItem == trackItem |
130 | + |
131 | + function play() { |
132 | + audioPlayer.stop(); |
133 | + // Make sure we change the source, even if two items point to the same uri location |
134 | + audioPlayer.source = ""; |
135 | + audioPlayer.source = model.uri; |
136 | + audioPlayer.playingItem = trackItem; |
137 | + audioPlayer.play(); |
138 | + } |
139 | + |
140 | + Row { |
141 | + id: trackRow |
142 | + width: parent.width |
143 | + spacing: units.gu(1) |
144 | + property int column1Width: units.gu(3) |
145 | + property int column2Width: width - (2 * spacing) - column1Width - column3Width |
146 | + property int column3Width: units.gu(4) |
147 | + anchors.verticalCenter: parent.verticalCenter |
148 | + |
149 | + Button { |
150 | + objectName: "playButton" |
151 | + width: trackRow.column1Width |
152 | + height: width |
153 | + iconSource: audioPlayer.playbackState == Audio.PlayingState && trackItem.isPlayingItem ? "image://theme/media-playback-pause" : "image://theme/media-playback-start" |
154 | + |
155 | + // Can't be "transparent" or "#00xxxxxx" as the button optimizes away the surrounding shape |
156 | + // FIXME when this is resolved: https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1251685 |
157 | + color: "#01000000" |
158 | + |
159 | + onClicked: { |
160 | + if (trackItem.isPlayingItem) { |
161 | + if (audioPlayer.playbackState == Audio.PlayingState) { |
162 | + audioPlayer.pause(); |
163 | + } else if (audioPlayer.playbackState == Audio.PausedState){ |
164 | + audioPlayer.play(); |
165 | + } |
166 | + } else { |
167 | + trackItem.play(); |
168 | + } |
169 | + } |
170 | + } |
171 | + |
172 | + Label { |
173 | + objectName: "trackTitleLabel" |
174 | + fontSize: "small" |
175 | + opacity: 0.9 |
176 | + color: "white" |
177 | + horizontalAlignment: Text.AlignLeft |
178 | + anchors.verticalCenter: parent.verticalCenter |
179 | + width: parent.column2Width |
180 | + text: title |
181 | + style: Text.Raised |
182 | + styleColor: "black" |
183 | + elide: Text.ElideRight |
184 | + |
185 | + UbuntuShape { |
186 | + id: progressBarFill |
187 | + objectName: "progressBarFill" |
188 | + color: UbuntuColors.orange |
189 | + anchors.left: progressBarImage.left |
190 | + anchors.right: progressBarImage.right |
191 | + anchors.verticalCenter: progressBarImage.verticalCenter |
192 | + height: units.dp(2) |
193 | + anchors.margins: units.dp(2) |
194 | + anchors.rightMargin: maxWidth - (maxWidth * audioPlayer.progress) + units.dp(2) |
195 | + visible: progressBarImage.visible |
196 | + property int maxWidth: progressBarImage.width - units.dp(4) |
197 | + } |
198 | + |
199 | + Image { |
200 | + id: progressBarImage |
201 | + anchors { left: parent.left; top: parent.bottom; right: parent.right } |
202 | + height: units.dp(6) |
203 | + visible: audioPlayer.playbackState != Audio.StoppedState && trackItem.isPlayingItem && model.length.length > 0 |
204 | + source: "graphics/music_progress_bg.png" |
205 | + } |
206 | + } |
207 | + |
208 | + Label { |
209 | + id: valueLabel |
210 | + objectName: "timeLabel" |
211 | + fontSize: "small" |
212 | + opacity: 0.9 |
213 | + color: "white" |
214 | + anchors.verticalCenter: parent.verticalCenter |
215 | + horizontalAlignment: Text.AlignRight |
216 | + width: parent.column3Width |
217 | + text: length |
218 | + style: Text.Raised |
219 | + styleColor: "black" |
220 | + } |
221 | + } |
222 | + |
223 | + ThinDivider { |
224 | + anchors { left: parent.left; bottom: parent.bottom; right: parent.right } |
225 | + } |
226 | + } |
227 | + } |
228 | + } |
229 | + } |
230 | + } |
231 | +} |
232 | |
233 | === added directory 'Dash/Music/graphics' |
234 | === added file 'Dash/Music/graphics/music_progress_bg.png' |
235 | Binary files Dash/Music/graphics/music_progress_bg.png 1970-01-01 00:00:00 +0000 and Dash/Music/graphics/music_progress_bg.png 2013-11-27 11:27:38 +0000 differ |
236 | === modified file 'Dash/PreviewDelegateMapper.qml' |
237 | --- Dash/PreviewDelegateMapper.qml 2013-10-31 15:22:22 +0000 |
238 | +++ Dash/PreviewDelegateMapper.qml 2013-11-27 11:27:38 +0000 |
239 | @@ -23,6 +23,7 @@ |
240 | property var previewDelegateMapping: {"preview-generic": genericPreview, |
241 | "preview-application": appPreview, |
242 | "preview-movie": "Movie/MoviePreview.qml", |
243 | + "preview-music": "Music/MusicPreview.qml", |
244 | } |
245 | } |
246 | |
247 | |
248 | === modified file 'debian/control' |
249 | --- debian/control 2013-11-25 22:21:23 +0000 |
250 | +++ debian/control 2013-11-27 11:27:38 +0000 |
251 | @@ -34,6 +34,7 @@ |
252 | qtbase5-private-dev, |
253 | qtdeclarative5-dev, |
254 | qtdeclarative5-dev-tools, |
255 | + qtdeclarative5-qtmultimedia-plugin, |
256 | qtdeclarative5-private-dev, |
257 | qtdeclarative5-qtquick2-plugin, |
258 | qtdeclarative5-test-plugin, |
259 | |
260 | === modified file 'tests/mocks/CMakeLists.txt' |
261 | --- tests/mocks/CMakeLists.txt 2013-10-16 14:23:03 +0000 |
262 | +++ tests/mocks/CMakeLists.txt 2013-11-27 11:27:38 +0000 |
263 | @@ -7,6 +7,7 @@ |
264 | add_subdirectory(QMenuModel) |
265 | add_subdirectory(Ubuntu) |
266 | add_subdirectory(Unity) |
267 | +add_subdirectory(QtMultimedia) |
268 | |
269 | install( |
270 | DIRECTORY data |
271 | |
272 | === added directory 'tests/mocks/QtMultimedia' |
273 | === added file 'tests/mocks/QtMultimedia/CMakeLists.txt' |
274 | --- tests/mocks/QtMultimedia/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
275 | +++ tests/mocks/QtMultimedia/CMakeLists.txt 2013-11-27 11:27:38 +0000 |
276 | @@ -0,0 +1,19 @@ |
277 | +add_library(QtMultimedia-qml MODULE |
278 | + plugin.cpp |
279 | + audio.cpp |
280 | + ) |
281 | + |
282 | +qt5_use_modules(QtMultimedia-qml Qml) |
283 | + |
284 | +# copy qmldir file into build directory for shadow builds |
285 | +file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/qmldir" |
286 | + DESTINATION ${CMAKE_CURRENT_BINARY_DIR} |
287 | + ) |
288 | + |
289 | +install(TARGETS QtMultimedia-qml |
290 | + DESTINATION ${SHELL_INSTALL_QML}/mocks/QtMultimedia |
291 | + ) |
292 | + |
293 | +install(FILES qmldir |
294 | + DESTINATION ${SHELL_INSTALL_QML}/mocks/QtMultimedia |
295 | + ) |
296 | |
297 | === added file 'tests/mocks/QtMultimedia/audio.cpp' |
298 | --- tests/mocks/QtMultimedia/audio.cpp 1970-01-01 00:00:00 +0000 |
299 | +++ tests/mocks/QtMultimedia/audio.cpp 2013-11-27 11:27:38 +0000 |
300 | @@ -0,0 +1,107 @@ |
301 | +/* |
302 | + * Copyright (C) 2013 Canonical, Ltd. |
303 | + * |
304 | + * This program is free software; you can redistribute it and/or modify |
305 | + * it under the terms of the GNU General Public License as published by |
306 | + * the Free Software Foundation; version 3. |
307 | + * |
308 | + * This program is distributed in the hope that it will be useful, |
309 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
310 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
311 | + * GNU General Public License for more details. |
312 | + * |
313 | + * You should have received a copy of the GNU General Public License |
314 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
315 | + * |
316 | + * Author: Michael Zanetti <michael.zanetti@canonical.com> |
317 | + */ |
318 | + |
319 | +#include "audio.h" |
320 | + |
321 | +Audio::Audio(QObject* parent): |
322 | + QObject(parent), |
323 | + m_playbackState(StoppedState) |
324 | +{ |
325 | + qsrand(time(NULL)); |
326 | + m_timer.setInterval(1000); |
327 | + connect(&m_timer, SIGNAL(timeout()), SLOT(timerEvent())); |
328 | +} |
329 | + |
330 | +QUrl Audio::source() const |
331 | +{ |
332 | + return m_source; |
333 | +} |
334 | + |
335 | +void Audio::setSource(const QUrl &source) |
336 | +{ |
337 | + if (m_source != source) { |
338 | + m_source = source; |
339 | + Q_EMIT sourceChanged(source); |
340 | + |
341 | + m_position = 0; |
342 | + Q_EMIT positionChanged(m_position); |
343 | + |
344 | + m_duration = (qrand() % 20000) + 10000; |
345 | + Q_EMIT durationChanged(m_duration); |
346 | + } |
347 | +} |
348 | + |
349 | +Audio::PlaybackState Audio::playbackState() const |
350 | +{ |
351 | + return m_playbackState; |
352 | +} |
353 | + |
354 | +int Audio::position() const |
355 | +{ |
356 | + return m_position; |
357 | +} |
358 | + |
359 | +int Audio::duration() const |
360 | +{ |
361 | + return m_duration; |
362 | +} |
363 | + |
364 | +QString Audio::errorString() const |
365 | +{ |
366 | + return QString(); |
367 | +} |
368 | + |
369 | +void Audio::pause() |
370 | +{ |
371 | + if (m_playbackState == PlayingState) { |
372 | + m_playbackState = PausedState; |
373 | + Q_EMIT playbackStateChanged(m_playbackState); |
374 | + m_timer.stop(); |
375 | + } |
376 | +} |
377 | + |
378 | +void Audio::play() |
379 | +{ |
380 | + if (m_playbackState != PlayingState && m_source.isValid()) { |
381 | + m_playbackState = PlayingState; |
382 | + Q_EMIT playbackStateChanged(m_playbackState); |
383 | + |
384 | + m_timer.start(); |
385 | + } |
386 | +} |
387 | + |
388 | +void Audio::stop() |
389 | +{ |
390 | + if (m_playbackState != StoppedState) { |
391 | + m_playbackState = StoppedState; |
392 | + Q_EMIT playbackStateChanged(m_playbackState); |
393 | + m_timer.stop(); |
394 | + m_position = 0; |
395 | + Q_EMIT positionChanged(m_position); |
396 | + } |
397 | +} |
398 | + |
399 | +void Audio::timerEvent() |
400 | +{ |
401 | + if (m_position + 1000 < m_duration) { |
402 | + m_position += 1000; |
403 | + Q_EMIT positionChanged(m_position); |
404 | + } else { |
405 | + stop(); |
406 | + } |
407 | +} |
408 | |
409 | === added file 'tests/mocks/QtMultimedia/audio.h' |
410 | --- tests/mocks/QtMultimedia/audio.h 1970-01-01 00:00:00 +0000 |
411 | +++ tests/mocks/QtMultimedia/audio.h 2013-11-27 11:27:38 +0000 |
412 | @@ -0,0 +1,78 @@ |
413 | +/* |
414 | + * Copyright (C) 2012,2013 Canonical, Ltd. |
415 | + * |
416 | + * This program is free software; you can redistribute it and/or modify |
417 | + * it under the terms of the GNU General Public License as published by |
418 | + * the Free Software Foundation; version 3. |
419 | + * |
420 | + * This program is distributed in the hope that it will be useful, |
421 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
422 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
423 | + * GNU General Public License for more details. |
424 | + * |
425 | + * You should have received a copy of the GNU General Public License |
426 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
427 | + * |
428 | + * Authors: Michael Zanetti <michael.zanetti@canonical.com> |
429 | + */ |
430 | + |
431 | +#ifndef MOCK_AUDIO_H |
432 | +#define MOCK_AUDIO_H |
433 | + |
434 | +#include <QObject> |
435 | +#include <QUrl> |
436 | +#include <QTimer> |
437 | + |
438 | +class Audio: public QObject |
439 | +{ |
440 | + Q_OBJECT |
441 | + Q_ENUMS(PlaybackState) |
442 | + Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) |
443 | + Q_PROPERTY(PlaybackState playbackState READ playbackState NOTIFY playbackStateChanged) |
444 | + Q_PROPERTY(int position READ position NOTIFY positionChanged) |
445 | + Q_PROPERTY(int duration READ duration NOTIFY durationChanged) |
446 | + Q_PROPERTY(QString errorString READ errorString NOTIFY errorStringChanged) |
447 | +public: |
448 | + enum PlaybackState { |
449 | + PlayingState, |
450 | + PausedState, |
451 | + StoppedState |
452 | + }; |
453 | + |
454 | + explicit Audio(QObject *parent = 0); |
455 | + |
456 | + QUrl source() const; |
457 | + void setSource(const QUrl &source); |
458 | + |
459 | + PlaybackState playbackState() const; |
460 | + |
461 | + int position() const; |
462 | + |
463 | + int duration() const; |
464 | + |
465 | + QString errorString() const; |
466 | + |
467 | +public Q_SLOTS: |
468 | + void pause(); |
469 | + void play(); |
470 | + void stop(); |
471 | + |
472 | +Q_SIGNALS: |
473 | + void sourceChanged(const QUrl &source); |
474 | + void playbackStateChanged(PlaybackState playbackState); |
475 | + void positionChanged(int position); |
476 | + void durationChanged(int duration); |
477 | + void errorStringChanged(const QString &errorString); |
478 | + |
479 | +private Q_SLOTS: |
480 | + void timerEvent(); |
481 | + |
482 | +private: |
483 | + QUrl m_source; |
484 | + PlaybackState m_playbackState; |
485 | + QTimer m_timer; |
486 | + int m_position; |
487 | + int m_duration; |
488 | +}; |
489 | + |
490 | +#endif |
491 | |
492 | === added file 'tests/mocks/QtMultimedia/plugin.cpp' |
493 | --- tests/mocks/QtMultimedia/plugin.cpp 1970-01-01 00:00:00 +0000 |
494 | +++ tests/mocks/QtMultimedia/plugin.cpp 2013-11-27 11:27:38 +0000 |
495 | @@ -0,0 +1,28 @@ |
496 | +/* |
497 | + * Copyright (C) 2012,2013 Canonical, Ltd. |
498 | + * |
499 | + * This program is free software; you can redistribute it and/or modify |
500 | + * it under the terms of the GNU General Public License as published by |
501 | + * the Free Software Foundation; version 3. |
502 | + * |
503 | + * This program is distributed in the hope that it will be useful, |
504 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
505 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
506 | + * GNU General Public License for more details. |
507 | + * |
508 | + * You should have received a copy of the GNU General Public License |
509 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
510 | + * |
511 | + * Authors: Michael Zanetti <michael.zanetti@canonical.com> |
512 | + */ |
513 | + |
514 | +#include "plugin.h" |
515 | +#include "audio.h" |
516 | + |
517 | +#include <QtQml/qqml.h> |
518 | + |
519 | +void MockQtMultimediaPlugin::registerTypes(const char *uri) |
520 | +{ |
521 | + Q_ASSERT(uri == QLatin1String("QtMultimedia")); |
522 | + qmlRegisterType<Audio>(uri, 5, 0, "Audio"); |
523 | +} |
524 | |
525 | === added file 'tests/mocks/QtMultimedia/plugin.h' |
526 | --- tests/mocks/QtMultimedia/plugin.h 1970-01-01 00:00:00 +0000 |
527 | +++ tests/mocks/QtMultimedia/plugin.h 2013-11-27 11:27:38 +0000 |
528 | @@ -0,0 +1,34 @@ |
529 | +/* |
530 | + * Copyright (C) 2012,2013 Canonical, Ltd. |
531 | + * |
532 | + * This program is free software; you can redistribute it and/or modify |
533 | + * it under the terms of the GNU General Public License as published by |
534 | + * the Free Software Foundation; version 3. |
535 | + * |
536 | + * This program is distributed in the hope that it will be useful, |
537 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
538 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
539 | + * GNU General Public License for more details. |
540 | + * |
541 | + * You should have received a copy of the GNU General Public License |
542 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
543 | + * |
544 | + * Authors: Michael Zanetti <michael.zanetti@canonical.com> |
545 | + */ |
546 | + |
547 | +#ifndef MOCK_QTMULTIMEDIA_PLUGIN_H |
548 | +#define MOCK_QTMULTIMEDIA_PLUGIN_H |
549 | + |
550 | +#include <QtQml/QQmlEngine> |
551 | +#include <QtQml/QQmlExtensionPlugin> |
552 | + |
553 | +class MockQtMultimediaPlugin : public QQmlExtensionPlugin |
554 | +{ |
555 | + Q_OBJECT |
556 | + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") |
557 | + |
558 | +public: |
559 | + void registerTypes(const char *uri); |
560 | +}; |
561 | + |
562 | +#endif |
563 | |
564 | === added file 'tests/mocks/QtMultimedia/qmldir' |
565 | --- tests/mocks/QtMultimedia/qmldir 1970-01-01 00:00:00 +0000 |
566 | +++ tests/mocks/QtMultimedia/qmldir 2013-11-27 11:27:38 +0000 |
567 | @@ -0,0 +1,2 @@ |
568 | +module QtMultimedia |
569 | +plugin QtMultimedia-qml |
570 | |
571 | === modified file 'tests/qmltests/CMakeLists.txt' |
572 | --- tests/qmltests/CMakeLists.txt 2013-11-19 09:43:35 +0000 |
573 | +++ tests/qmltests/CMakeLists.txt 2013-11-27 11:27:38 +0000 |
574 | @@ -49,6 +49,7 @@ |
575 | add_qml_test(Dash/Apps RunningApplicationsGrid IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks) |
576 | add_qml_test(Dash/Apps AppPreview IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks) |
577 | add_qml_test(Dash/Movie MoviePreview IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks) |
578 | +add_qml_test(Dash/Music MusicPreview IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks) |
579 | add_qml_test(Greeter Lockscreen IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS} |
580 | PROPERTIES ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full") |
581 | add_qml_test(Greeter Tablet IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS} |
582 | |
583 | === added directory 'tests/qmltests/Dash/Music' |
584 | === added file 'tests/qmltests/Dash/Music/tst_MusicPreview.qml' |
585 | --- tests/qmltests/Dash/Music/tst_MusicPreview.qml 1970-01-01 00:00:00 +0000 |
586 | +++ tests/qmltests/Dash/Music/tst_MusicPreview.qml 2013-11-27 11:27:38 +0000 |
587 | @@ -0,0 +1,174 @@ |
588 | +/* |
589 | + * Copyright 2013 Canonical Ltd. |
590 | + * |
591 | + * This program is free software; you can redistribute it and/or modify |
592 | + * it under the terms of the GNU General Public License as published by |
593 | + * the Free Software Foundation; version 3. |
594 | + * |
595 | + * This program is distributed in the hope that it will be useful, |
596 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
597 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
598 | + * GNU General Public License for more details. |
599 | + * |
600 | + * You should have received a copy of the GNU General Public License |
601 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
602 | + */ |
603 | + |
604 | +import QtQuick 2.0 |
605 | +import QtTest 1.0 |
606 | +import "../../../../Dash/Music" |
607 | +import Unity.Test 0.1 as UT |
608 | +import QtMultimedia 5.0 |
609 | + |
610 | +Rectangle { |
611 | + id: root |
612 | + width: units.gu(60) |
613 | + height: units.gu(80) |
614 | + color: "lightgrey" |
615 | + |
616 | + MusicPreview { |
617 | + id: musicPreview |
618 | + anchors.fill: parent |
619 | + isCurrent: true |
620 | + |
621 | + previewData: QtObject { |
622 | + property string rendererName: "preview-music" |
623 | + property string title: "Music Preview" |
624 | + property string subtitle: "Subtitle" |
625 | + property string description: "This is the description" |
626 | + property string image: "../../tests/qmltests/Components/tst_LazyImage/square.png" |
627 | + property var actions: [ |
628 | + { id: 123, displayName: "Play"}, |
629 | + { id: 456, displayName: "Show in folder"} |
630 | + ] |
631 | + property var tracks: tracksModel2 |
632 | + } |
633 | + } |
634 | + |
635 | + ListModel { |
636 | + id: tracksModel0 |
637 | + } |
638 | + ListModel { |
639 | + id: tracksModel1 |
640 | + ListElement { uri: "../../tests/qmltests/Dash/Music/data/testsound1.ogg"; trackNo: 1; title: "Some track name"; length: "0:30"} |
641 | + } |
642 | + ListModel { |
643 | + id: tracksModel2 |
644 | + ListElement { uri: "../../tests/qmltests/Dash/Music/data/testsound1.ogg"; trackNo: 1; title: "Some track name"; length: "0:30"} |
645 | + ListElement { uri: "../../tests/qmltests/Dash/Music/data/testsound1.ogg"; trackNo: 2; title: "Some track name"; length: "0:30"} |
646 | + ListElement { uri: "../../tests/qmltests/Dash/Music/data/testsound2.ogg"; trackNo: 3; title: "Some other track name"; length: "1:23"} |
647 | + ListElement { uri: "../../tests/qmltests/Dash/Music/data/testsound3.ogg"; trackNo: 4; title: "And another one"; length: "123:45"} |
648 | + } |
649 | + |
650 | + UT.UnityTestCase { |
651 | + name: "MusicPreviewTest" |
652 | + when: windowShown |
653 | + |
654 | + function init() { |
655 | + waitForRendering(musicPreview); |
656 | + } |
657 | + |
658 | + function test_tracks_data() { |
659 | + return [ |
660 | + {tag: "0 tracks", tracksModel: tracksModel0, dividerVisible: false}, |
661 | + {tag: "1 track", tracksModel: tracksModel1, dividerVisible: true}, |
662 | + {tag: "4 track", tracksModel: tracksModel2, dividerVisible: true} |
663 | + ]; |
664 | + } |
665 | + |
666 | + function test_tracks(data) { |
667 | + musicPreview.previewData.tracks = data.tracksModel; |
668 | + waitForRendering(musicPreview); |
669 | + |
670 | + var trackRepeater = findChild(musicPreview, "trackRepeater"); |
671 | + compare(trackRepeater.count, data.tracksModel.count) |
672 | + |
673 | + var topDivider = findChild(musicPreview, "topDivider"); |
674 | + compare(topDivider.visible, data.dividerVisible); |
675 | + |
676 | + for (var i = 0; i < data.tracksModel.count; ++i) { |
677 | + var trackItem = findChild(musicPreview, "trackItem" + i); |
678 | + var titleLabel = findChild(trackItem, "trackTitleLabel"); |
679 | + compare(titleLabel.text, data.tracksModel.get(i).title) |
680 | + var timeLabel = findChild(trackItem, "timeLabel"); |
681 | + compare(timeLabel.text, data.tracksModel.get(i).length) |
682 | + } |
683 | + } |
684 | + |
685 | + function checkPlayerUri(index) { |
686 | + var modelFilename = musicPreview.previewData.tracks.get(index).uri.replace(/^.*[\\\/]/, ''); |
687 | + var playerFilename = findInvisibleChild(musicPreview, "audioPlayer").source.toString().replace(/^.*[\\\/]/, ''); |
688 | + |
689 | + compare(modelFilename, playerFilename, "Player source is not set correctly."); |
690 | + } |
691 | + |
692 | + function test_playback() { |
693 | + musicPreview.previewData.tracks = tracksModel2; |
694 | + waitForRendering(musicPreview); |
695 | + |
696 | + var track0Item = findChild(musicPreview, "trackItem0"); |
697 | + var track1Item = findChild(musicPreview, "trackItem1"); |
698 | + var track2Item = findChild(musicPreview, "trackItem2"); |
699 | + |
700 | + var track0ProgressBar = findChild(track0Item, "progressBarFill"); |
701 | + var track1ProgressBar = findChild(track1Item, "progressBarFill"); |
702 | + var track2ProgressBar = findChild(track2Item, "progressBarFill"); |
703 | + |
704 | + var track0PlayButton = findChild(track0Item, "playButton"); |
705 | + var track1PlayButton = findChild(track1Item, "playButton"); |
706 | + var track2PlayButton = findChild(track2Item, "playButton"); |
707 | + |
708 | + var audioPlayer = findInvisibleChild(musicPreview, "audioPlayer"); |
709 | + |
710 | + // All progress bars must be hidden in the beginning |
711 | + compare(track0ProgressBar.visible, false); |
712 | + compare(track1ProgressBar.visible, false); |
713 | + compare(track2ProgressBar.visible, false); |
714 | + |
715 | + // Playing track 0 should make progress bar 0 visible |
716 | + mouseClick(track0PlayButton, track0PlayButton.width / 2, track0PlayButton.height / 2); |
717 | + |
718 | + tryCompare(audioPlayer, "playbackState", Audio.PlayingState); |
719 | + checkPlayerUri(0); |
720 | + |
721 | + tryCompare(track0ProgressBar, "visible", true); |
722 | + tryCompare(track1ProgressBar, "visible", false); |
723 | + tryCompare(track2ProgressBar, "visible", false); |
724 | + |
725 | + // Clicking the button again should pause it. The progress bar should stay visible |
726 | + mouseClick(track0PlayButton, track0PlayButton.width / 2, track0PlayButton.height / 2); |
727 | + tryCompare(audioPlayer, "playbackState", Audio.PausedState); |
728 | + checkPlayerUri(0); |
729 | + tryCompare(track0ProgressBar, "visible", true); |
730 | + |
731 | + // Continue playback |
732 | + mouseClick(track0PlayButton, track0PlayButton.width / 2, track0PlayButton.height / 2); |
733 | + tryCompare(audioPlayer, "playbackState", Audio.PlayingState); |
734 | + checkPlayerUri(0); |
735 | + |
736 | + // Playing track 1 should make progress bar 1 visible and hide progress bar 0 again |
737 | + mouseClick(track1PlayButton, track1PlayButton.width / 2, track1PlayButton.height / 2); |
738 | + |
739 | + tryCompare(audioPlayer, "playbackState", Audio.PlayingState); |
740 | + checkPlayerUri(1); |
741 | + |
742 | + tryCompare(track0ProgressBar, "visible", false); |
743 | + tryCompare(track1ProgressBar, "visible", true); |
744 | + tryCompare(track2ProgressBar, "visible", false); |
745 | + |
746 | + // Playing track 2 should make progress bar 1 visible and hide progress bar 0 again |
747 | + mouseClick(track2PlayButton, track2PlayButton.width / 2, track2PlayButton.height / 2); |
748 | + |
749 | + tryCompare(audioPlayer, "playbackState", Audio.PlayingState); |
750 | + checkPlayerUri(2); |
751 | + |
752 | + tryCompare(track0ProgressBar, "visible", false); |
753 | + tryCompare(track1ProgressBar, "visible", false); |
754 | + tryCompare(track2ProgressBar, "visible", true); |
755 | + |
756 | + // Switching away from this preview should make all players shut up! |
757 | + musicPreview.isCurrent = false |
758 | + tryCompare(audioPlayer, "playbackState", Audio.StoppedState); |
759 | + } |
760 | + } |
761 | +} |
762 | |
763 | === modified file 'tests/qmltests/Dash/tst_GenericScopeView.qml' |
764 | --- tests/qmltests/Dash/tst_GenericScopeView.qml 2013-11-19 10:44:14 +0000 |
765 | +++ tests/qmltests/Dash/tst_GenericScopeView.qml 2013-11-27 11:27:38 +0000 |
766 | @@ -186,8 +186,18 @@ |
767 | true); |
768 | tryCompare(nextPreviewItem, "progress", 1); |
769 | waitForRendering(nextPreviewItem); |
770 | + tryCompareFunction(function() {return nextPreviewItem.item !== null}, true); |
771 | |
772 | checkArrowPosition(i); |
773 | + |
774 | + // Make sure only the new one has isCurrent set to true |
775 | + compare(nextPreviewItem.item.isCurrent, true); |
776 | + |
777 | + if (currentPreviewItem.item !== undefined && currentPreviewItem.item !== null) { |
778 | + compare(currentPreviewItem.item.isCurrent, false); |
779 | + } |
780 | + |
781 | + currentPreviewItem = nextPreviewItem; |
782 | } |
783 | closePreview(); |
784 | } |
Looking good overall, visually there's one separator missing between the orange button and the tracks, other than that I was able to to get lots of:
Error: "GStreamer encountered a general stream error."
...if I swiped slowly between two albums and returned to the first after revealing the second. Then playing tracks no longer worked, and the one that was playing didn't stop when closing the preview nor when swiping to different previews.
168 + onPositionChanged: print("got percent", percent, position, duration)
Remove pls...
233 +MusicPreviewTr ackModel *MusicPreview: :tracks( ) const
MusicPreviewTra ckModel* pls, looks wrong this way. :P
And as last thing, some ui tests would be nice? :)