on xenial help sign in
Create the DashAudioPlayer
9d12c18Albert Astals Cid
qml/Dash/DashAudioPlayer/DashAudioPlayer.qmlnew file+63
@@ -0,0 +1,63 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+pragma Singleton
18+import QtQuick 2.4
19+
20+import QtMultimedia 5.0
21+
22+QtObject {
23+ readonly property real progress: audio.position / audio.duration
24+ readonly property bool playing: audio.playbackState === Audio.PlayingState
25+ readonly property bool paused: audio.playbackState === Audio.PausedState
26+ readonly property bool stopped: audio.playbackState === Audio.StoppedState
27+
28+ property alias source: audio.source
29+
30+ function stop() {
31+ audio.stop();
32+ }
33+
34+ function play() {
35+ audio.play();
36+ }
37+
38+ function pause() {
39+ audio.pause();
40+ }
41+
42+ property QtObject d: Audio {
43+ id: audio
44+ objectName: "audio"
45+
46+ onErrorStringChanged: console.warn("Dash Audio player error:", errorString)
47+ }
48+
49+ function lengthToString(s) {
50+ if (typeof(s) !== "number" || s <= 0) return "";
51+
52+ var sec = "" + s % 60;
53+ if (sec.length == 1) sec = "0" + sec;
54+ var hour = Math.floor(s / 3600);
55+ if (hour < 1) {
56+ return Math.floor(s / 60) + ":" + sec;
57+ } else {
58+ var min = "" + Math.floor(s / 60) % 60;
59+ if (min.length == 1) min = "0" + min;
60+ return hour + ":" + min + ":" + sec;
61+ }
62+ }
63+}
qml/Dash/DashAudioPlayer/qmldirnew file+1
@@ -0,0 +1 @@
1+singleton DashAudioPlayer 1.0 DashAudioPlayer.qml
qml/Dash/Previews/PreviewAudioPlayback.qml+20−52
@@ -15,8 +15,8 @@
15 */
15 */
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18−import QtMultimedia 5.0
19import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19+import "../DashAudioPlayer"
20
20
21/*! \brief Preview widget for audio tracks.
21/*! \brief Preview widget for audio tracks.
22
22
@@ -36,38 +36,6 @@ PreviewWidget {
36 id: root
36 id: root
37 height: childrenRect.height
37 height: childrenRect.height
38
38
39− onIsCurrentPreviewChanged: if (!isCurrentPreview) audio.stop()
40−
41− Audio {
42− id: audio
43− objectName: "audio"
44−
45− property real progress: audio.position / audio.duration
46− property Item playingItem
47−
48− Component.onDestruction: {
49− // destroying the component doesn't automatically send stop to the media service, probably a bug in QtMultimedia
50− audio.stop();
51− }
52−
53− onErrorStringChanged: console.warn("Audio player error:", errorString)
54−
55− function lengthToString(s) {
56− if (typeof(s) !== "number" || s <= 0) return "";
57−
58− var sec = "" + s % 60;
59− if (sec.length == 1) sec = "0" + sec;
60− var hour = Math.floor(s / 3600);
61− if (hour < 1) {
62− return Math.floor(s / 60) + ":" + sec;
63− } else {
64− var min = "" + Math.floor(s / 60) % 60;
65− if (min.length == 1) min = "0" + min;
66− return hour + ":" + min + ":" + sec;
67− }
68− }
69− }
70−
71 Column {
39 Column {
72 anchors { left: parent.left; right: parent.right }
40 anchors { left: parent.left; right: parent.right }
73 visible: trackRepeater.count > 0
41 visible: trackRepeater.count > 0
@@ -78,19 +46,19 @@ PreviewWidget {
78 model: root.widgetData["tracks"]
46 model: root.widgetData["tracks"]
79
47
80 function play(item, source) {
48 function play(item, source) {
81− audio.stop();
49+ DashAudioPlayer.stop();
82 // Make sure we change the source, even if two items point to the same uri location
50 // Make sure we change the source, even if two items point to the same uri location
83− audio.source = "";
51+ DashAudioPlayer.source = "";
84− audio.source = source;
52+ DashAudioPlayer.source = source;
85− audio.playingItem = item;
53+ DashAudioPlayer.play();
86− audio.play();
87 }
54 }
88
55
89 delegate: Item {
56 delegate: Item {
90 id: trackItem
57 id: trackItem
91 objectName: "trackItem" + index
58 objectName: "trackItem" + index
92
59
60+ readonly property url sourceUrl: modelData["source"]
93− property bool isPlayingItem: audio.playingItem == trackItem
61+ readonly property bool isPlayingItem: DashAudioPlayer.source == sourceUrl
94
62
95 anchors { left: parent.left; right: parent.right }
63 anchors { left: parent.left; right: parent.right }
96 height: units.gu(5)
64 height: units.gu(5)
@@ -98,9 +66,9 @@ PreviewWidget {
98 Row {
66 Row {
99 id: trackRow
67 id: trackRow
100
68
101− property int column1Width: units.gu(3)
69+ readonly property int column1Width: units.gu(3)
102− property int column2Width: width - (2 * spacing) - column1Width - column3Width
70+ readonly property int column2Width: width - (2 * spacing) - column1Width - column3Width
103− property int column3Width: units.gu(4)
71+ readonly property int column3Width: units.gu(4)
104
72
105 anchors.verticalCenter: parent.verticalCenter
73 anchors.verticalCenter: parent.verticalCenter
106 width: parent.width
74 width: parent.width
@@ -110,7 +78,7 @@ PreviewWidget {
110 objectName: "playButton"
78 objectName: "playButton"
111 width: trackRow.column1Width
79 width: trackRow.column1Width
112 height: width
80 height: width
113− iconSource: audio.playbackState == Audio.PlayingState && trackItem.isPlayingItem ? "image://theme/media-playback-pause" : "image://theme/media-playback-start"
81+ iconSource: DashAudioPlayer.playing && trackItem.isPlayingItem ? "image://theme/media-playback-pause" : "image://theme/media-playback-start"
114
82
115 // Can't be "transparent" or "#00xxxxxx" as the button optimizes away the surrounding shape
83 // Can't be "transparent" or "#00xxxxxx" as the button optimizes away the surrounding shape
116 // FIXME when this is resolved: https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1251685
84 // FIXME when this is resolved: https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1251685
@@ -118,13 +86,13 @@ PreviewWidget {
118
86
119 onClicked: {
87 onClicked: {
120 if (trackItem.isPlayingItem) {
88 if (trackItem.isPlayingItem) {
121− if (audio.playbackState == Audio.PlayingState) {
89+ if (DashAudioPlayer.playing) {
122− audio.pause();
90+ DashAudioPlayer.pause();
123− } else if (audio.playbackState == Audio.PausedState) {
91+ } else if (DashAudioPlayer.paused) {
124− audio.play();
92+ DashAudioPlayer.play();
125 }
93 }
126 } else {
94 } else {
127− trackRepeater.play(trackItem, modelData["source"]);
95+ trackRepeater.play(trackItem, sourceUrl);
128 }
96 }
129 }
97 }
130 }
98 }
@@ -164,14 +132,14 @@ PreviewWidget {
164 id: progressBarFill
132 id: progressBarFill
165 objectName: "progressBarFill"
133 objectName: "progressBarFill"
166
134
167− property int maxWidth: progressBarImage.width - units.dp(4)
135+ readonly property int maxWidth: progressBarImage.width - units.dp(4)
168
136
169 anchors {
137 anchors {
170 left: progressBarImage.left
138 left: progressBarImage.left
171 right: progressBarImage.right
139 right: progressBarImage.right
172 verticalCenter: progressBarImage.verticalCenter
140 verticalCenter: progressBarImage.verticalCenter
173 margins: units.dp(2)
141 margins: units.dp(2)
174− rightMargin: maxWidth - (maxWidth * audio.progress) + units.dp(2)
142+ rightMargin: maxWidth - (maxWidth * DashAudioPlayer.progress) + units.dp(2)
175 }
143 }
176 height: units.dp(2)
144 height: units.dp(2)
177 visible: progressBarImage.visible
145 visible: progressBarImage.visible
@@ -182,7 +150,7 @@ PreviewWidget {
182 id: progressBarImage
150 id: progressBarImage
183 anchors { left: parent.left; top: parent.bottom; right: parent.right }
151 anchors { left: parent.left; top: parent.bottom; right: parent.right }
184 height: units.dp(6)
152 height: units.dp(6)
185− visible: audio.playbackState != Audio.StoppedState && trackItem.isPlayingItem && modelData["length"] > 0
153+ visible: !DashAudioPlayer.stopped && trackItem.isPlayingItem && modelData["length"] > 0
186 source: "graphics/music_progress_bg.png"
154 source: "graphics/music_progress_bg.png"
187 }
155 }
188 }
156 }
@@ -196,7 +164,7 @@ PreviewWidget {
196 color: scopeStyle ? scopeStyle.foreground : Theme.palette.normal.baseText
164 color: scopeStyle ? scopeStyle.foreground : Theme.palette.normal.baseText
197 fontSize: "small"
165 fontSize: "small"
198 horizontalAlignment: Text.AlignRight
166 horizontalAlignment: Text.AlignRight
199− text: audio.lengthToString(modelData["length"])
167+ text: DashAudioPlayer.lengthToString(modelData["length"])
200 }
168 }
201 }
169 }
202 }
170 }
tests/qmltests/Dash/Previews/tst_PreviewAudioPlayback.qml+4−8
@@ -18,6 +18,7 @@ import QtQuick 2.0
18import QtTest 1.0
18import QtTest 1.0
19import Ubuntu.Components 0.1
19import Ubuntu.Components 0.1
20import "../../../../qml/Dash/Previews"
20import "../../../../qml/Dash/Previews"
21+import "../../../../qml/Dash/DashAudioPlayer"
21import Unity.Test 0.1 as UT
22import Unity.Test 0.1 as UT
22import QtMultimedia 5.0
23import QtMultimedia 5.0
23
24
@@ -67,8 +68,7 @@ Rectangle {
67 }
68 }
68
69
69 function test_time_formatter(data) {
70 function test_time_formatter(data) {
70− var audio = findInvisibleChild(previewAudioPlayback, "audio");
71− compare(audio.lengthToString(data.value), data.result)
71+ compare(DashAudioPlayer.lengthToString(data.value), data.result)
72 }
72 }
73
73
74 function test_tracks_data() {
74 function test_tracks_data() {
@@ -102,7 +102,7 @@ Rectangle {
102
102
103 function checkPlayerSource(index) {
103 function checkPlayerSource(index) {
104 var modelFilename = previewAudioPlayback.widgetData["tracks"][index]["source"].replace(/^.*[\\\/]/, '');
104 var modelFilename = previewAudioPlayback.widgetData["tracks"][index]["source"].replace(/^.*[\\\/]/, '');
105− var playerFilename = findInvisibleChild(previewAudioPlayback, "audio").source.toString().replace(/^.*[\\\/]/, '');
105+ var playerFilename = DashAudioPlayer.source.toString().replace(/^.*[\\\/]/, '');
106
106
107 compare(modelFilename, playerFilename, "Player source is not set correctly.");
107 compare(modelFilename, playerFilename, "Player source is not set correctly.");
108 }
108 }
@@ -123,7 +123,7 @@ Rectangle {
123 var track1PlayButton = findChild(track1Item, "playButton");
123 var track1PlayButton = findChild(track1Item, "playButton");
124 var track2PlayButton = findChild(track2Item, "playButton");
124 var track2PlayButton = findChild(track2Item, "playButton");
125
125
126− var audio = findInvisibleChild(previewAudioPlayback, "audio");
126+ var audio = DashAudioPlayer.d;
127
127
128 // All progress bars must be hidden in the beginning
128 // All progress bars must be hidden in the beginning
129 compare(track0ProgressBar.visible, false);
129 compare(track0ProgressBar.visible, false);
@@ -170,10 +170,6 @@ Rectangle {
170 tryCompare(track0ProgressBar, "visible", false);
170 tryCompare(track0ProgressBar, "visible", false);
171 tryCompare(track1ProgressBar, "visible", false);
171 tryCompare(track1ProgressBar, "visible", false);
172 tryCompare(track2ProgressBar, "visible", true);
172 tryCompare(track2ProgressBar, "visible", true);
173−
174− // Changing preview should make all players shut up!
175− previewAudioPlayback.isCurrentPreview = false
176− tryCompare(audio, "playbackState", Audio.StoppedState);
177 }
173 }
178 }
174 }
179}
175}