Merge lp:~artmello/ubuntu-ui-extras/ubuntu-ui-extras-videoplayer-components into lp:ubuntu-ui-extras

Proposed by Arthur Mello
Status: Needs review
Proposed branch: lp:~artmello/ubuntu-ui-extras/ubuntu-ui-extras-videoplayer-components
Merge into: lp:ubuntu-ui-extras
Diff against target: 1320 lines (+1258/-0)
11 files modified
modules/Ubuntu/Components/Extras/CMakeLists.txt (+1/-0)
modules/Ubuntu/Components/Extras/MediaPlayer/AbstractPlayer.qml (+213/-0)
modules/Ubuntu/Components/Extras/MediaPlayer/CMakeLists.txt (+2/-0)
modules/Ubuntu/Components/Extras/MediaPlayer/ContentPopover.qml (+63/-0)
modules/Ubuntu/Components/Extras/MediaPlayer/PlayerActionBar.qml (+83/-0)
modules/Ubuntu/Components/Extras/MediaPlayer/PlayerActionsDrawer.qml (+124/-0)
modules/Ubuntu/Components/Extras/MediaPlayer/PlayerPanel.qml (+111/-0)
modules/Ubuntu/Components/Extras/MediaPlayer/TimeLine.qml (+98/-0)
modules/Ubuntu/Components/Extras/MediaPlayer/VideoPlayerToolbar.qml (+128/-0)
modules/Ubuntu/Components/Extras/VideoPlayer.qml (+434/-0)
modules/Ubuntu/Components/Extras/qmldir (+1/-0)
To merge this branch: bzr merge lp:~artmello/ubuntu-ui-extras/ubuntu-ui-extras-videoplayer-components
Reviewer Review Type Date Requested Status
system-apps-ci-bot continuous-integration Needs Fixing
PS Jenkins bot continuous-integration Approve
Ubuntu Phablet Team Pending
Review via email: mp+280450@code.launchpad.net

Commit message

Add VideoPlayer Component

Description of the change

Add VideoPlayer Component

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
74. By Arthur Mello

Add property to set title
Add back action

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
75. By Arthur Mello

Fix video replay after ending

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
76. By Arthur Mello

Merge with trunk

77. By Arthur Mello

Make sure to stop player before closing the stream

78. By Arthur Mello

Move title bar to a loader so it will not show up during component loading

79. By Arthur Mello

Fix toolbars open/close

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
80. By Arthur Mello

Changes to botton bar:
- Change Share button for a Play/Pause one
- Add more vertical space between time labels and scrubber
- Add button anchored to the right to toggle fullscreen

81. By Arthur Mello

Changes to titlebar
- Add share/save buttons by default
- Application can control if share/save buttons are visible
- Application can add it owns actions to the titlebar

82. By Arthur Mello

Fix toolbar actions issue

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
83. By Arthur Mello

Make sure the dismissBars timer is not triggered twice

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
84. By Arthur Mello

Do not trigger the same action twice

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
85. By Arthur Mello

Fix Share/Save actions so the ContentPeerPicker can be called from inside the component

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
86. By Arthur Mello

Fix behavior when the Video ends

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
87. By Arthur Mello

Handle fullscreen propoty inside video player

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
88. By Arthur Mello

Fix typo

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
89. By Arthur Mello

Add elite to title bar text

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
90. By Arthur Mello

Merge with trunk

91. By Arthur Mello

Change component API following review

92. By Arthur Mello

Make fullscreen a rdonly property

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
system-apps-ci-bot (system-apps-ci-bot) wrote :
Download full text (3.3 KiB)

FAILED: Continuous integration, rev:92
https://jenkins.canonical.com/system-apps/job/lp-ubuntu-ui-extras-ci/1/
Executed test runs:
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build/640
    FAILURE: https://jenkins.canonical.com/system-apps/job/test-0-autopkgtest/label=phone-armhf,release=vivid+overlay,testname=default/84/console
    FAILURE: https://jenkins.canonical.com/system-apps/job/test-0-autopkgtest/label=phone-armhf,release=xenial+overlay,testname=default/84/console
    FAILURE: https://jenkins.canonical.com/system-apps/job/test-0-autopkgtest/label=phone-armhf,release=yakkety,testname=default/84/console
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-0-fetch/640
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-1-sourcepkg/release=vivid+overlay/607
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-1-sourcepkg/release=xenial+overlay/607
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-1-sourcepkg/release=yakkety/607
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=amd64,release=vivid+overlay/599
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=amd64,release=vivid+overlay/599/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=amd64,release=xenial+overlay/599
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=amd64,release=xenial+overlay/599/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=amd64,release=yakkety/599
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=amd64,release=yakkety/599/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=armhf,release=vivid+overlay/599
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=armhf,release=vivid+overlay/599/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=armhf,release=xenial+overlay/599
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=armhf,release=xenial+overlay/599/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=armhf,release=yakkety/599
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=armhf,release=yakkety/599/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=i386,release=vivid+overlay/599
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=i386,release=vivid+overlay/599/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=i386,release=xenial+overlay/599
        deb: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=i386,release=xenial+overlay/599/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/system-apps/job/build-2-binpkg/arch=i386,release=yakkety/599
        deb: https://jenkins.canonical.com/system-apps/job/build...

Read more...

review: Needs Fixing (continuous-integration)

Unmerged revisions

92. By Arthur Mello

Make fullscreen a rdonly property

91. By Arthur Mello

Change component API following review

90. By Arthur Mello

Merge with trunk

89. By Arthur Mello

Add elite to title bar text

88. By Arthur Mello

Fix typo

87. By Arthur Mello

Handle fullscreen propoty inside video player

86. By Arthur Mello

Fix behavior when the Video ends

85. By Arthur Mello

Fix Share/Save actions so the ContentPeerPicker can be called from inside the component

84. By Arthur Mello

Do not trigger the same action twice

83. By Arthur Mello

Make sure the dismissBars timer is not triggered twice

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'modules/Ubuntu/Components/Extras/CMakeLists.txt'
2--- modules/Ubuntu/Components/Extras/CMakeLists.txt 2014-12-09 11:14:16 +0000
3+++ modules/Ubuntu/Components/Extras/CMakeLists.txt 2016-03-09 10:31:35 +0000
4@@ -21,4 +21,5 @@
5
6 add_subdirectory(plugin)
7 add_subdirectory(Example)
8+add_subdirectory(MediaPlayer)
9 add_subdirectory(PhotoEditor)
10
11=== added directory 'modules/Ubuntu/Components/Extras/MediaPlayer'
12=== added file 'modules/Ubuntu/Components/Extras/MediaPlayer/AbstractPlayer.qml'
13--- modules/Ubuntu/Components/Extras/MediaPlayer/AbstractPlayer.qml 1970-01-01 00:00:00 +0000
14+++ modules/Ubuntu/Components/Extras/MediaPlayer/AbstractPlayer.qml 2016-03-09 10:31:35 +0000
15@@ -0,0 +1,213 @@
16+/*
17+ * Copyright (C) 2013-2015 Canonical, Ltd.
18+ *
19+ * Authors:
20+ * Ugo Riboni <ugo.riboni@canonical.com>
21+ * Michał Sawicz <michal.sawicz@canonical.com>
22+ * Renato Araujo Oliveira Filho <renato@canonical.com>
23+ * Arthur Renato Mello <arthur.mello@canonical.com>
24+ *
25+ * This program is free software; you can redistribute it and/or modify
26+ * it under the terms of the GNU General Public License as published by
27+ * the Free Software Foundation; version 3.
28+ *
29+ * This program is distributed in the hope that it will be useful,
30+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
31+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32+ * GNU General Public License for more details.
33+ *
34+ * You should have received a copy of the GNU General Public License
35+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
36+ */
37+
38+import QtQuick 2.4
39+import QtMultimedia 5.0
40+
41+Item {
42+ id: player
43+
44+ property MediaPlayer mediaPlayer: _mediaPlayer
45+ property alias source: _mediaPlayer.source
46+
47+ property int forwardSeekStep: Math.min(60000, _mediaPlayer.duration * 0.05)
48+ property int backwardSeekStep: Math.min(30000, _mediaPlayer.duration * 0.025)
49+
50+
51+ signal error(int errorCode, string errorString)
52+
53+ state: "stopped"
54+
55+ function stop() {
56+ state = "stopped"
57+ }
58+
59+ function play() {
60+ state = "playing"
61+ }
62+
63+ function pause() {
64+ state = "paused"
65+ }
66+
67+ function togglePause() {
68+ if (playing) {
69+ pause()
70+ } else if (paused) {
71+ play()
72+ }
73+ }
74+
75+ function seekForward() {
76+ return seek(forwardSeekStep)
77+ }
78+
79+ function seekBackward() {
80+ return seek(-backwardSeekStep)
81+ }
82+
83+ function seek(value) {
84+ if (_mediaPlayer.seekable) {
85+ if (state != "playing" && state != "paused") {
86+ state = "playing"
87+ state = "paused"
88+ }
89+ _mediaPlayer.seek(_mediaPlayer.position + value)
90+ } else {
91+ return false
92+ }
93+ return true
94+ }
95+
96+ function fastForward() {
97+ if (_mediaPlayer.seekable) {
98+ if (state == "forwarding") {
99+ scrubbingTimer.step = Math.min(5 * forwardSeekStep, scrubbingTimer.step * 1.5)
100+ } else {
101+ state = "forwarding"
102+ }
103+ return true
104+ } else {
105+ return false
106+ }
107+ }
108+
109+ function rewind() {
110+ if (_mediaPlayer.seekable) {
111+ if (state == "rewinding") {
112+ scrubbingTimer.step = Math.max(-5 * forwardSeekStep, scrubbingTimer.step * 1.5)
113+ } else {
114+ state = "rewinding"
115+ }
116+ return true
117+ } else {
118+ return false
119+ }
120+ }
121+
122+ function hideVideoOutput() {
123+ _videoOutput.visible = false
124+ }
125+
126+ function showVideoOutput() {
127+ _videoOutput.visible = true
128+ }
129+
130+ Rectangle {
131+ id: bg
132+ anchors.fill: parent
133+ color: "#1B1B1B"
134+ opacity: 0.85
135+ }
136+
137+ VideoOutput {
138+ id: _videoOutput
139+
140+ source: _mediaPlayer
141+ anchors.fill: parent
142+ smooth: true
143+
144+ Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.OutQuad } }
145+ }
146+
147+ MediaPlayer {
148+ id: _mediaPlayer
149+
150+ onError: {
151+ console.error("AbstractPlayer: " + error + ":" + errorString)
152+ player.error(error, errorString)
153+ }
154+
155+ onPlaybackStateChanged: {
156+ // Make sure that the app toggles the play/pause button when playbackStatus
157+ // changes from underneath it in media-hub/qtubuntu-media
158+ if (_mediaPlayer.playbackState == MediaPlayer.PausedState) {
159+ player.pause()
160+ } else if (_mediaPlayer.playbackState == MediaPlayer.PlayingState) {
161+ player.play()
162+ }
163+ }
164+ }
165+
166+ Timer {
167+ id: scrubbingTimer
168+ interval: 500
169+ repeat: true
170+
171+ property int step
172+
173+ onTriggered:
174+ if (_mediaPlayer.position + step < 0) {
175+ _mediaPlayer.seek(0)
176+ player.state = "playing"
177+ } else {
178+ _mediaPlayer.seek(_mediaPlayer.position + step)
179+ }
180+ }
181+
182+ states: [
183+ State {
184+ name: "stopped"
185+ StateChangeScript { script: _mediaPlayer.stop() }
186+ PropertyChanges { target: scrubbingTimer; running: false }
187+ },
188+
189+ State {
190+ name: "playing"
191+ PropertyChanges { target: _mediaPlayer; playbackRate: 1.0; muted: false }
192+ StateChangeScript { script: _mediaPlayer.play() }
193+ PropertyChanges { target: scrubbingTimer; running: false }
194+ },
195+
196+ State {
197+ name: "paused"
198+ StateChangeScript { script: _mediaPlayer.pause() }
199+ PropertyChanges { target: scrubbingTimer; running: false }
200+ },
201+
202+ State {
203+ name: "forwarding"
204+ PropertyChanges { target: _mediaPlayer; muted: true }
205+ StateChangeScript { script: _mediaPlayer.pause() }
206+ PropertyChanges { target: scrubbingTimer; running: true }
207+ },
208+
209+ State {
210+ name: "rewinding"
211+ PropertyChanges { target: _mediaPlayer; muted: true }
212+ StateChangeScript { script: _mediaPlayer.pause() }
213+ PropertyChanges { target: scrubbingTimer; running: true }
214+ }
215+ ]
216+
217+ transitions: [
218+ Transition {
219+ to: "forwarding"
220+ PropertyAction { target: scrubbingTimer; property: "step"; value: forwardSeekStep }
221+ },
222+
223+ Transition {
224+ to: "rewinding"
225+ PropertyAction { target: scrubbingTimer; property: "step"; value: -forwardSeekStep }
226+ }
227+ ]
228+}
229
230=== added file 'modules/Ubuntu/Components/Extras/MediaPlayer/CMakeLists.txt'
231--- modules/Ubuntu/Components/Extras/MediaPlayer/CMakeLists.txt 1970-01-01 00:00:00 +0000
232+++ modules/Ubuntu/Components/Extras/MediaPlayer/CMakeLists.txt 2016-03-09 10:31:35 +0000
233@@ -0,0 +1,2 @@
234+file(GLOB QML_FILES *.qml *.js)
235+install(FILES ${QML_FILES} DESTINATION ${PLUGIN_DIR}/MediaPlayer)
236
237=== added file 'modules/Ubuntu/Components/Extras/MediaPlayer/ContentPopover.qml'
238--- modules/Ubuntu/Components/Extras/MediaPlayer/ContentPopover.qml 1970-01-01 00:00:00 +0000
239+++ modules/Ubuntu/Components/Extras/MediaPlayer/ContentPopover.qml 2016-03-09 10:31:35 +0000
240@@ -0,0 +1,63 @@
241+/*
242+ * Copyright 2014 Canonical Ltd.
243+ *
244+ * This program is free software; you can redistribute it and/or modify
245+ * it under the terms of the GNU General Public License as published by
246+ * the Free Software Foundation; version 3.
247+ *
248+ * This program is distributed in the hope that it will be useful,
249+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
250+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
251+ * GNU General Public License for more details.
252+ *
253+ * You should have received a copy of the GNU General Public License
254+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
255+ */
256+
257+import QtQuick 2.4
258+import Ubuntu.Components 1.3
259+import Ubuntu.Components.Popups 1.3
260+import Ubuntu.Content 0.1
261+
262+PopupBase {
263+ id: contentPopover
264+
265+ property var transferContentType
266+ property var transferItems
267+
268+ property alias transferContentHandler: contentPeerPicker.handler
269+
270+ signal contentPeerSelected()
271+
272+ fadingAnimation: UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration }
273+
274+ Component.onCompleted: {
275+ contentPeerPicker.peerSelected.connect(contentPeerSelected);
276+ }
277+
278+ Rectangle {
279+ anchors.fill: parent
280+ color: theme.palette.normal.overlay
281+ }
282+
283+ ContentPeerPicker {
284+ id: contentPeerPicker
285+
286+ Component.onCompleted: {
287+ contentType = parent.transferContentType;
288+ }
289+
290+ visible: true
291+
292+ onPeerSelected: {
293+ var transfer = peer.request();
294+ if (transfer.state === ContentTransfer.InProgress) {
295+ transfer.items = parent.transferItems;
296+ transfer.state = ContentTransfer.Charged;
297+ }
298+ PopupUtils.close(contentPopover);
299+ }
300+ onCancelPressed: PopupUtils.close(contentPopover);
301+ }
302+}
303+
304
305=== added file 'modules/Ubuntu/Components/Extras/MediaPlayer/PlayerActionBar.qml'
306--- modules/Ubuntu/Components/Extras/MediaPlayer/PlayerActionBar.qml 1970-01-01 00:00:00 +0000
307+++ modules/Ubuntu/Components/Extras/MediaPlayer/PlayerActionBar.qml 2016-03-09 10:31:35 +0000
308@@ -0,0 +1,83 @@
309+/*
310+ * Copyright (C) 2016 Canonical, Ltd.
311+ *
312+ * This program is free software; you can redistribute it and/or modify
313+ * it under the terms of the GNU General Public License as published by
314+ * the Free Software Foundation; version 3.
315+ *
316+ * This program is distributed in the hope that it will be useful,
317+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
318+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
319+ * GNU General Public License for more details.
320+ *
321+ * You should have received a copy of the GNU General Public License
322+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
323+ */
324+
325+import QtQuick 2.4
326+import Ubuntu.Components 1.3
327+
328+Item {
329+ id: playerActionBar
330+
331+ property list<Action> actions
332+
333+ signal drawerOpened()
334+ signal drawerClosed()
335+
336+ function closeDrawer() {
337+ actionsDrawer.close()
338+ }
339+
340+ width: units.gu(5)
341+ height: width
342+
343+ AbstractButton {
344+ id: singleActionButton
345+
346+ anchors.fill: parent
347+ visible: playerActionBar.actions.length == 1
348+ action: playerActionBar.actions.length == 1 ? playerActionBar.actions[0] : null
349+
350+ Icon {
351+ anchors.centerIn: parent
352+ width: units.gu(2.5)
353+ height: width
354+
355+ color: "white"
356+ name: singleActionButton.action ? singleActionButton.action.iconName : ""
357+ opacity: singleActionButton.action ? (singleActionButton.action.enabled ? 1.0 : 0.5) : 1.0
358+ }
359+ }
360+
361+ AbstractButton {
362+ onClicked: {
363+ if (actionsDrawer.visible) {
364+ actionsDrawer.close()
365+ } else {
366+ actionsDrawer.open()
367+ }
368+ }
369+
370+ anchors.fill: parent
371+ visible: playerActionBar.actions.length > 1
372+
373+ Icon {
374+ anchors.centerIn: parent
375+ width: units.gu(2.5)
376+ height: width
377+
378+ color: "white"
379+ name: "contextual-menu"
380+ }
381+ }
382+
383+ PlayerActionsDrawer {
384+ id: actionsDrawer
385+
386+ onDrawerOpened: playerActionBar.drawerOpened()
387+ onDrawerClosed: playerActionBar.drawerClosed()
388+
389+ actions: playerActionBar.actions
390+ }
391+}
392
393=== added file 'modules/Ubuntu/Components/Extras/MediaPlayer/PlayerActionsDrawer.qml'
394--- modules/Ubuntu/Components/Extras/MediaPlayer/PlayerActionsDrawer.qml 1970-01-01 00:00:00 +0000
395+++ modules/Ubuntu/Components/Extras/MediaPlayer/PlayerActionsDrawer.qml 2016-03-09 10:31:35 +0000
396@@ -0,0 +1,124 @@
397+/*
398+ * Copyright (C) 2016 Canonical, Ltd.
399+ *
400+ * This program is free software; you can redistribute it and/or modify
401+ * it under the terms of the GNU General Public License as published by
402+ * the Free Software Foundation; version 3.
403+ *
404+ * This program is distributed in the hope that it will be useful,
405+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
406+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
407+ * GNU General Public License for more details.
408+ *
409+ * You should have received a copy of the GNU General Public License
410+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
411+ */
412+
413+import QtQuick 2.4
414+import Ubuntu.Components 1.3
415+
416+Item {
417+ id: actionsDrawer
418+
419+ property list<Action> actions
420+ property bool opened: false
421+
422+ signal drawerOpened()
423+ signal drawerClosed()
424+
425+ function open() {
426+ opened = true;
427+ visible = true;
428+ actionsDrawer.drawerOpened()
429+ }
430+
431+ function close() {
432+ opened = false;
433+ actionsDrawer.drawerClosed()
434+ }
435+
436+ anchors {
437+ top: parent.bottom
438+ right: parent.right
439+ }
440+
441+ width: units.gu(20)
442+ height: actionsColumn.height
443+ visible: false
444+
445+ clip: actionsColumn.y != 0
446+
447+ Column {
448+ id: actionsColumn
449+
450+ onYChanged: {
451+ if (y == -height) {
452+ actionsDrawer.visible = false;
453+ }
454+ }
455+
456+ anchors {
457+ left: parent.left
458+ right: parent.right
459+ }
460+
461+ y: actionsDrawer.opened ? 0 : -height
462+ Behavior on y { UbuntuNumberAnimation {} }
463+
464+ Repeater {
465+ model: actionsDrawer.actions.length > 0 ? actionsDrawer.actions : 0
466+
467+ delegate: AbstractButton {
468+ id: actionButton
469+
470+ anchors {
471+ left: actionsColumn.left
472+ right: actionsColumn.right
473+ }
474+
475+ height: units.gu(6)
476+ enabled: action.enabled
477+
478+ action: modelData
479+ onClicked: actionsDrawer.close()
480+
481+ Rectangle {
482+ anchors.fill: parent
483+ color: actionButton.pressed ? Qt.rgba(1.0, 1.0, 1.0, 0.3) : Qt.rgba(0.0, 0.0, 0.0, 0.6)
484+ }
485+
486+ Label {
487+ id: label
488+
489+ anchors {
490+ left: icon.right
491+ right: parent.right
492+ leftMargin: units.gu(2)
493+ rightMargin: units.gu(2)
494+ verticalCenter: parent.verticalCenter
495+ }
496+
497+ text: model.text
498+ elide: Text.ElideRight
499+ color: action.enabled ? theme.palette.normal.foregroundText : Qt.darker(theme.palette.normal.foregroundText, 2.0)
500+ }
501+
502+ Icon {
503+ id: icon
504+
505+ anchors {
506+ left: parent.left
507+ leftMargin: units.gu(2)
508+ verticalCenter: parent.verticalCenter
509+ }
510+
511+ width: height
512+ height: label.paintedHeight
513+
514+ color: label.color
515+ name: model.iconName
516+ }
517+ }
518+ }
519+ }
520+}
521
522=== added file 'modules/Ubuntu/Components/Extras/MediaPlayer/PlayerPanel.qml'
523--- modules/Ubuntu/Components/Extras/MediaPlayer/PlayerPanel.qml 1970-01-01 00:00:00 +0000
524+++ modules/Ubuntu/Components/Extras/MediaPlayer/PlayerPanel.qml 2016-03-09 10:31:35 +0000
525@@ -0,0 +1,111 @@
526+/*
527+ * Copyright (C) 2015 Canonical, Ltd.
528+ *
529+ * Authors:
530+ * Arthur Renato Mello <arthur.mello@canonical.com>
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+
545+import QtQuick 2.4
546+import Ubuntu.Components 1.3
547+
548+Panel {
549+ id: playerPanel
550+
551+ property Item panelContents
552+
553+ readonly property alias leadingActionBar: leading
554+ readonly property alias trailingActionBar: trailing
555+
556+ signal panelDrawerOpened()
557+ signal panelDrawerClosed()
558+
559+ Component.onCompleted: holder.updateContents()
560+ onPanelContentsChanged: holder.updateContents()
561+
562+ onOpenedChanged: {
563+ if (!opened) {
564+ leading.closeDrawer()
565+ trailing.closeDrawer()
566+ }
567+ }
568+
569+ Rectangle {
570+ anchors.fill: parent
571+ color: "#1B1B1B"
572+ opacity: 0.85
573+
574+ PlayerActionBar {
575+ id: leading
576+
577+ onDrawerOpened: playerPanel.panelDrawerOpened()
578+ onDrawerClosed: playerPanel.panelDrawerClosed()
579+
580+ anchors {
581+ top: parent.top
582+ bottom: parent.bottom
583+ left: parent.left
584+ leftMargin: units.gu(1)
585+ }
586+
587+ visible: actions.length > 0
588+ }
589+
590+ Item {
591+ id: holder
592+
593+ property Item previousContents: null
594+ property Item previousContentsParent: null
595+
596+ anchors {
597+ top: parent.top
598+ bottom: parent.bottom
599+ left: leading.right
600+ right: trailing.left
601+ margins: units.gu(0.5)
602+ }
603+
604+ function updateContents() {
605+ if (holder.previousContents) {
606+ holder.previousContents.parent = holder.previousContentsParent;
607+ }
608+
609+ if (playerPanel.panelContents) {
610+ holder.previousContents = playerPanel.panelContents;
611+ holder.previousContentsParent = playerPanel.panelContents.parent;
612+ playerPanel.panelContents.parent = holder;
613+ } else {
614+ holder.previousContents = null;
615+ holder.previousContentsParent = null;
616+ }
617+ }
618+ }
619+
620+ PlayerActionBar {
621+ id: trailing
622+
623+ onDrawerOpened: playerPanel.panelDrawerOpened()
624+ onDrawerClosed: playerPanel.panelDrawerClosed()
625+
626+ anchors {
627+ top: parent.top
628+ bottom: parent.bottom
629+ right: parent.right
630+ rightMargin: units.gu(1)
631+ }
632+
633+ visible: actions.length > 0
634+ }
635+ }
636+}
637
638=== added file 'modules/Ubuntu/Components/Extras/MediaPlayer/TimeLine.qml'
639--- modules/Ubuntu/Components/Extras/MediaPlayer/TimeLine.qml 1970-01-01 00:00:00 +0000
640+++ modules/Ubuntu/Components/Extras/MediaPlayer/TimeLine.qml 2016-03-09 10:31:35 +0000
641@@ -0,0 +1,98 @@
642+/*
643+ * Copyright (C) 2013-2015 Canonical, Ltd.
644+ *
645+ * Authors:
646+ * Renato Araujo Oliveira Filho <renato@canonical.com>
647+ * Arthur Renato Mello <arthur.mello@canonical.com>
648+ *
649+ * This program is free software; you can redistribute it and/or modify
650+ * it under the terms of the GNU General Public License as published by
651+ * the Free Software Foundation; version 3.
652+ *
653+ * This program is distributed in the hope that it will be useful,
654+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
655+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
656+ * GNU General Public License for more details.
657+ *
658+ * You should have received a copy of the GNU General Public License
659+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
660+ */
661+
662+import QtQuick 2.4
663+import Ubuntu.Components 1.3
664+
665+Item {
666+ id: _sliderItem
667+
668+ readonly property alias liveValue: _slider.value
669+ readonly property alias pressed: _slider.pressed
670+
671+ property alias minimumValue: _slider.minimumValue
672+ property alias maximumValue: _slider.maximumValue
673+
674+ property real videoPosition: -1
675+ property string currentTime
676+ property string remainingTime
677+ property string durationTime
678+
679+ onVideoPositionChanged: {
680+ if (!_slider.pressed) {
681+ _slider.value = _sliderItem.videoPosition
682+ }
683+ }
684+
685+ Slider {
686+ id: _slider
687+ objectName: "TimeLine.Slider"
688+
689+ function formatValue(v) {
690+ var hour = 0
691+ var min = 0
692+ var secs = 0
693+ var timeValue = Math.floor(v)
694+
695+ secs = timeValue % 60
696+ timeValue = Math.floor(timeValue / 60)
697+ min = timeValue % 60
698+ hour = Math.floor(timeValue / 60)
699+
700+ if (secs < 10) secs = "0%1".arg(secs)
701+ if (min < 10) min = "0%1".arg(min)
702+ if (hour < 10) hour = "0%1".arg(hour)
703+
704+ // TRANSLATORS: this refers to a duration/remaining time of the video, of which you can change the order.
705+ // %1 refers to hours, %2 refers to minutes and %3 refers to seconds.
706+ return i18n.tr("%1:%2:%3").arg(hour).arg(min).arg(secs)
707+ }
708+
709+ anchors {
710+ left: parent.left
711+ right: parent.right
712+ }
713+
714+ live: true
715+
716+ onValueChanged: {
717+ if (value > 0) {
718+ _sliderItem.currentTime = formatValue(value)
719+ if (_slider.maximumValue > 0) {
720+ _sliderItem.remainingTime = formatValue(_slider.maximumValue - value)
721+ } else {
722+ // TRANSLATORS: this refers to an unknown duration.
723+ _sliderItem.remainingTime = i18n.tr("unknown")
724+ }
725+ } else {
726+ _sliderItem.currentTime = i18n.tr("0:00:00")
727+ }
728+ }
729+
730+ onMaximumValueChanged: {
731+ if (_slider.maximumValue > 0) {
732+ _sliderItem.durationTime = _slider.formatValue(_slider.maximumValue)
733+ } else {
734+ // TRANSLATORS: this refers to an unknown duration.
735+ _sliderItem.remainingTime = i18n.tr("unknown")
736+ }
737+ }
738+ }
739+}
740
741=== added file 'modules/Ubuntu/Components/Extras/MediaPlayer/VideoPlayerToolbar.qml'
742--- modules/Ubuntu/Components/Extras/MediaPlayer/VideoPlayerToolbar.qml 1970-01-01 00:00:00 +0000
743+++ modules/Ubuntu/Components/Extras/MediaPlayer/VideoPlayerToolbar.qml 2016-03-09 10:31:35 +0000
744@@ -0,0 +1,128 @@
745+/*
746+ * Copyright (C) 2015 Canonical, Ltd.
747+ *
748+ * Authors:
749+ * Arthur Renato Mello <arthur.mello@canonical.com>
750+ *
751+ * This program is free software; you can redistribute it and/or modify
752+ * it under the terms of the GNU General Public License as published by
753+ * the Free Software Foundation; version 3.
754+ *
755+ * This program is distributed in the hope that it will be useful,
756+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
757+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
758+ * GNU General Public License for more details.
759+ *
760+ * You should have received a copy of the GNU General Public License
761+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
762+ */
763+
764+import QtQuick 2.4
765+import QtMultimedia 5.0
766+import Ubuntu.Components 1.3
767+
768+PlayerPanel {
769+ id: _videoPlayerToolbar
770+
771+ property var videoPlayerStatus
772+ property var videoPlayerState
773+ property real videoDuration: 1000
774+ property real videoPosition: 0
775+
776+ readonly property bool isPlaying: _videoPlayerToolbar.videoPlayerState == "playing"
777+ readonly property bool isPaused: _videoPlayerToolbar.videoPlayerState == "paused"
778+ readonly property bool isEndOfMedia: _videoPlayerToolbar.videoPlayerStatus != MediaPlayer.EndOfMedia
779+
780+ signal playRequested()
781+ signal pauseRequested()
782+ signal seekRequested(int time)
783+ signal fullscreenActionRequested()
784+
785+ leadingActionBar.actions: [
786+ Action {
787+ iconName: _videoPlayerToolbar.isPlaying ? "media-playback-pause" : "media-playback-start"
788+ text: _videoPlayerToolbar.isPlaying ? i18n.tr("Pause") : i18n.tr("Start")
789+ onTriggered: {
790+ if (_videoPlayerToolbar.isPlaying) {
791+ _videoPlayerToolbar.pauseRequested()
792+ } else {
793+ _videoPlayerToolbar.playRequested()
794+ }
795+ }
796+ }
797+ ]
798+
799+ panelContents: Item {
800+ anchors.fill: parent
801+
802+ TimeLine {
803+ id: _timeline
804+
805+ property bool seeking: false
806+
807+ anchors {
808+ top: parent.top
809+ left: parent.left
810+ right: parent.right
811+ }
812+
813+ height: units.gu(4)
814+
815+ minimumValue: 0
816+ maximumValue: _videoPlayerToolbar.videoDuration
817+ videoPosition: _videoPlayerToolbar.videoPosition
818+
819+ // Pause the video during the seek
820+ onPressedChanged: {
821+ if (!pressed && seeking) {
822+ if (_videoPlayerToolbar.isEndOfMedia &&
823+ _videoPlayerToolbar.isPaused) {
824+
825+ _videoPlayerToolbar.playRequested()
826+ }
827+ seeking = false
828+ }
829+ }
830+
831+ // Live value is the real slider value. Ex: User dragging the slider
832+ onLiveValueChanged: {
833+ var changed = Math.abs(liveValue - videoPosition)
834+ if (changed > 1) {
835+ if (!seeking) {
836+ _videoPlayerToolbar.pauseRequested()
837+ seeking = true
838+ }
839+ _videoPlayerToolbar.seekRequested(liveValue * 1000)
840+ }
841+ }
842+ }
843+
844+ Label {
845+ anchors {
846+ top: _timeline.bottom
847+ left: parent.left
848+ }
849+
850+ text: _timeline.currentTime
851+ fontSize: "x-small"
852+ }
853+
854+ Label {
855+ anchors {
856+ top: _timeline.bottom
857+ right: parent.right
858+ }
859+
860+ text: _timeline.durationTime
861+ fontSize: "x-small"
862+ }
863+ }
864+
865+ trailingActionBar.actions: [
866+ Action {
867+ iconName: "view-fullscreen"
868+ text: i18n.tr("Toggle Fullscreen")
869+ onTriggered: _videoPlayerToolbar.fullscreenActionRequested()
870+ }
871+ ]
872+}
873
874=== added file 'modules/Ubuntu/Components/Extras/VideoPlayer.qml'
875--- modules/Ubuntu/Components/Extras/VideoPlayer.qml 1970-01-01 00:00:00 +0000
876+++ modules/Ubuntu/Components/Extras/VideoPlayer.qml 2016-03-09 10:31:35 +0000
877@@ -0,0 +1,434 @@
878+/*
879+ * Copyright (C) 2013-2015 Canonical, Ltd.
880+ *
881+ * Authors:
882+ * Ugo Riboni <ugo.riboni@canonical.com>
883+ * Michał Sawicz <michal.sawicz@canonical.com>
884+ * Renato Araujo Oliveira Filho <renato@canonical.com>
885+ * Arthur Renato Mello <arthur.mello@canonical.com>
886+ *
887+ * This program is free software; you can redistribute it and/or modify
888+ * it under the terms of the GNU General Public License as published by
889+ * the Free Software Foundation; version 3.
890+ *
891+ * This program is distributed in the hope that it will be useful,
892+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
893+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
894+ * GNU General Public License for more details.
895+ *
896+ * You should have received a copy of the GNU General Public License
897+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
898+ */
899+
900+import QtQuick 2.4
901+import QtMultimedia 5.0
902+import Ubuntu.Components 1.3
903+import Ubuntu.Components.Extras 0.2
904+import Ubuntu.Components.Popups 1.0 as Popups
905+import Ubuntu.Content 0.1
906+import Ubuntu.Thumbnailer 0.1
907+import "MediaPlayer"
908+
909+AbstractPlayer {
910+ id: player
911+ objectName: "player"
912+
913+ signal closeRequested()
914+
915+ readonly property int seekStep: 1000
916+ readonly property bool fullscreen: true
917+
918+ property list<Action> titlebarActions
919+ property var errorDialog: null
920+ property string title: ""
921+
922+ function playPause() {
923+ if (["paused", "playing"].indexOf(player.state) != -1) {
924+ player.togglePause();
925+ } else {
926+ player.play();
927+ }
928+ }
929+
930+ function setTitlebarOpened(opened) {
931+ if (!_titlebarLoader.item) {
932+ return
933+ }
934+
935+ if (opened && !_titlebarLoader.item.opened) {
936+ _titlebarLoader.item.open()
937+ } else if (!opened && _titlebarLoader.item.opened) {
938+ _titlebarLoader.item.close()
939+ }
940+ }
941+
942+ function setToolbarOpened(opened) {
943+ if (!_toolbarLoader.item) {
944+ return
945+ }
946+
947+ if (opened && !_toolbarLoader.item.opened) {
948+ _toolbarLoader.item.open()
949+ } else if (!opened && _toolbarLoader.item.opened) {
950+ _toolbarLoader.item.close()
951+ }
952+ }
953+
954+ Component.onCompleted: {
955+ if ((state !== "playing") && (source != "")) {
956+ play()
957+ }
958+ }
959+
960+ Connections {
961+ target: player.mediaPlayer
962+ onStatusChanged: {
963+ if (status == MediaPlayer.EndOfMedia) {
964+ player.fullscreen = false
965+ player.hideVideoOutput()
966+ player.pause()
967+ player.seek(0)
968+ player.stop()
969+ player.setTitlebarOpened(true)
970+ player.setToolbarOpened(true)
971+ } else {
972+ player.showVideoOutput()
973+ }
974+ }
975+ }
976+
977+ MouseArea {
978+ id: _mouseArea
979+ objectName: "videoMouseArea"
980+
981+ anchors.fill: parent
982+
983+ onClicked: {
984+ if (!_titlebarLoader.item || !_toolbarLoader.item) {
985+ return
986+ }
987+
988+ player.setTitlebarOpened(!_titlebarLoader.item.opened)
989+ player.setToolbarOpened(!_toolbarLoader.item.opened)
990+ }
991+ }
992+
993+ Timer {
994+ id: _dismissBars
995+
996+ running: false
997+ interval: 1000
998+ repeat: false
999+ onTriggered: {
1000+ player.setTitlebarOpened(false)
1001+ player.setToolbarOpened(false)
1002+ }
1003+ }
1004+
1005+ Image {
1006+ id: _videoThumbnail
1007+
1008+ fillMode: Image.PreserveAspectCrop
1009+ smooth: true
1010+ source: "image://thumbnailer/" + player.source.toString().replace("file://", "")
1011+
1012+ visible: player.mediaPlayer.status == MediaPlayer.EndOfMedia
1013+ opacity: visible ? 1.0 : 0.0
1014+ Behavior on opacity { UbuntuNumberAnimation {} }
1015+
1016+ asynchronous: true
1017+ height: parent.height
1018+ width: parent.width
1019+ cache: false
1020+
1021+ sourceSize.width: parent.width
1022+ sourceSize.height: parent.height
1023+
1024+ Icon {
1025+ width: units.gu(5)
1026+ height: units.gu(5)
1027+ anchors.centerIn: parent
1028+ name: "media-playback-start"
1029+ color: "white"
1030+ opacity: 0.8
1031+ }
1032+
1033+ MouseArea {
1034+ anchors.fill: parent
1035+ onClicked: {
1036+ player.fullscreen = true
1037+ player.play()
1038+ player.setTitlebarOpened(false)
1039+ player.setToolbarOpened(false)
1040+ }
1041+ }
1042+ }
1043+
1044+ Loader {
1045+ id: _titlebarLoader
1046+
1047+ anchors {
1048+ top: parent.top
1049+ left: parent.left
1050+ right: parent.right
1051+ }
1052+
1053+ height: units.gu(6)
1054+
1055+ active: player.mediaPlayer && player.mediaPlayer.duration > 0
1056+ sourceComponent: _titlebarComponent
1057+ }
1058+
1059+ Component {
1060+ id: _titlebarComponent
1061+
1062+ PlayerPanel {
1063+ id: _titlebar
1064+
1065+ onPanelDrawerOpened: _dismissBars.stop()
1066+ onPanelDrawerClosed: {
1067+ if (_titlebar.opened) {
1068+ _dismissBars.restart()
1069+ }
1070+ }
1071+
1072+ Component.onCompleted: {
1073+ var newActions = [];
1074+ for (var i = 0; i < trailingActionBar.actions.length; i++) newActions.push(trailingActionBar.actions[i]);
1075+ for (var i = 0; i < player.titlebarActions.length; i++) newActions.push(player.titlebarActions[i]);
1076+ trailingActionBar.actions = newActions;
1077+ }
1078+
1079+ Action {
1080+ text: i18n.tr("Edit")
1081+ iconName: "edit"
1082+ }
1083+
1084+ align: Qt.AlignTop
1085+
1086+ leadingActionBar.actions: [
1087+ Action {
1088+ iconName: "back"
1089+ text: i18n.tr("Back")
1090+ onTriggered: {
1091+ player.stop()
1092+ player.closeRequested()
1093+ }
1094+ }
1095+ ]
1096+
1097+ panelContents: Label {
1098+ anchors {
1099+ left: parent.left
1100+ right: parent.right
1101+ verticalCenter: parent.verticalCenter
1102+ }
1103+
1104+ text: player.title != "" ? player.title : player.source
1105+ fontSize: "medium"
1106+ elide: Text.ElideRight
1107+ }
1108+
1109+ trailingActionBar.actions: [
1110+ Action {
1111+ iconName: "share"
1112+ text: i18n.tr("Share")
1113+ onTriggered: {
1114+ player.pause()
1115+ var dialog = PopupUtils.open(_sharePopoverComponent)
1116+ dialog.parent = player
1117+ }
1118+ },
1119+ Action {
1120+ iconName: "save"
1121+ text: i18n.tr("Save")
1122+ onTriggered: {
1123+ player.pause()
1124+ var dialog = PopupUtils.open(_savePopoverComponent)
1125+ dialog.parent = player
1126+ }
1127+ }
1128+ ]
1129+ }
1130+ }
1131+
1132+ Loader {
1133+ id: _toolbarLoader
1134+
1135+ anchors {
1136+ left: parent.left
1137+ right: parent.right
1138+ bottom: parent.bottom
1139+ }
1140+
1141+ height: units.gu(7)
1142+
1143+ active: player.mediaPlayer && player.mediaPlayer.duration > 0
1144+ sourceComponent: _toolbarComponent
1145+ }
1146+
1147+ Component {
1148+ id: _toolbarComponent
1149+
1150+ VideoPlayerToolbar {
1151+ id: _controls
1152+ objectName: "toolbar"
1153+
1154+ onPanelDrawerOpened: _dismissBars.stop()
1155+ onPanelDrawerClosed: {
1156+ if (_controls.opened) {
1157+ _dismissBars.restart()
1158+ }
1159+ }
1160+
1161+ onPlayRequested: {
1162+ player.play()
1163+ _dismissBars.restart()
1164+ }
1165+ onPauseRequested: player.pause()
1166+ onSeekRequested: player.mediaPlayer.seek(time)
1167+ onFullscreenActionRequested: player.fullscreen = !player.fullscreen
1168+
1169+ anchors.fill: parent
1170+
1171+ videoPlayerStatus: player.mediaPlayer.status
1172+ videoPlayerState: player.state
1173+
1174+ videoDuration: player.mediaPlayer.duration / 1000
1175+ videoPosition: player.mediaPlayer.position / 1000
1176+ }
1177+ }
1178+
1179+ Keys.onPressed: {
1180+ switch(event.key) {
1181+ case Qt.Key_Space:
1182+ player.playPause()
1183+ break;
1184+ case Qt.Key_Right:
1185+ case Qt.Key_Left:
1186+ {
1187+ var currentPos = (video ? video.position : 0)
1188+ var nextPos = currentPos
1189+ if (event.key == Qt.Key_Right) {
1190+ var maxPos = (video ? video.duration : 0)
1191+ nextPos += player.seekStep
1192+ if (nextPos > maxPos) {
1193+ nextPos = -1;
1194+ }
1195+ } else {
1196+ nextPos -= player.seekStep
1197+ if (nextPos < 0) {
1198+ nextPos = -1
1199+ }
1200+ }
1201+
1202+ if (nextPos != -1) {
1203+ player.mediaPlayer.seek(nextPos)
1204+ }
1205+ break;
1206+ }
1207+ case Qt.Key_F11:
1208+ player.fullscreen = !player.fullscreen
1209+ break;
1210+ case Qt.Key_Escape:
1211+ player.fullscreen = false
1212+ break;
1213+ default:
1214+ break;
1215+ }
1216+ }
1217+
1218+ Component {
1219+ id: _sharePopoverComponent
1220+
1221+ ContentPopover {
1222+ ContentItem { id: _contentItem }
1223+
1224+ Component.onCompleted: {
1225+ _contentItem.url = player.source
1226+ transferItems = [contentItem]
1227+ }
1228+
1229+ onVisibleChanged: {
1230+ player.setTitlebarOpened(true)
1231+ player.setToolbarOpened(true)
1232+ }
1233+
1234+ transferContentType: ContentType.Videos
1235+ transferContentHandler: ContentHandler.Share
1236+ }
1237+ }
1238+
1239+ Component {
1240+ id: _savePopoverComponent
1241+
1242+ ContentPopover {
1243+ ContentItem { id: _contentItem }
1244+
1245+ Component.onCompleted: {
1246+ _contentItem.url = player.source
1247+ transferItems = [_contentItem]
1248+ }
1249+
1250+ onVisibleChanged: {
1251+ player.setTitlebarOpened(true)
1252+ player.setToolbarOpened(true)
1253+ }
1254+
1255+ transferContentType: ContentType.Videos
1256+ transferContentHandler: ContentHandler.Destination
1257+ }
1258+ }
1259+
1260+ Component {
1261+ id: _dialogPlayerError
1262+
1263+ Popups.Dialog {
1264+ id: dialogue
1265+ objectName: "playError"
1266+
1267+ property string errorString: ""
1268+
1269+ title: i18n.tr("Error playing video")
1270+ text: errorString
1271+
1272+ Button {
1273+ text: i18n.tr("Close")
1274+ onClicked: {
1275+ PopupUtils.close(dialogue)
1276+ if (player.state != MediaPlayer.PlayingState) {
1277+ console.debug("Warning: Quitting app due to fatal playback error.")
1278+ Qt.quit()
1279+ }
1280+ }
1281+ }
1282+
1283+ Component.onDestruction: player.errorDialog = null
1284+ }
1285+ }
1286+
1287+ onError: {
1288+ if (player.errorDialog !== null) {
1289+ return
1290+ }
1291+
1292+ player.errorDialog = PopupUtils.open(_dialogPlayerError, null)
1293+ switch(errorCode) {
1294+ case 1:
1295+ player.errorDialog.errorString = i18n.tr("Fail to open the source video.")
1296+ break;
1297+ case 2:
1298+ player.errorDialog.errorString = i18n.tr("Video format not supported.")
1299+ break;
1300+ case 3:
1301+ player.errorDialog.errorString = i18n.tr("A network error occurred.")
1302+ break;
1303+ case 4:
1304+ player.errorDialog.errorString = i18n.tr("There are not the appropriate permissions to play a media resource.")
1305+ break;
1306+ case 5:
1307+ player.errorDialog.errorString = i18n.tr("Fail to connect with playback backend.")
1308+ break;
1309+ }
1310+ }
1311+}
1312
1313=== modified file 'modules/Ubuntu/Components/Extras/qmldir'
1314--- modules/Ubuntu/Components/Extras/qmldir 2015-02-11 14:46:34 +0000
1315+++ modules/Ubuntu/Components/Extras/qmldir 2016-03-09 10:31:35 +0000
1316@@ -2,3 +2,4 @@
1317 plugin ubuntu-ui-extras-plugin
1318 Example 0.2 Example.qml
1319 PhotoEditor 0.2 PhotoEditor.qml
1320+VideoPlayer 0.2 VideoPlayer.qml

Subscribers

People subscribed via source and target branches