Merge lp:~renatofilho/mediaplayer-app/pocket-pc into lp:mediaplayer-app

Proposed by Renato Araujo Oliveira Filho
Status: Merged
Approved by: Bill Filler
Approved revision: 398
Merged at revision: 386
Proposed branch: lp:~renatofilho/mediaplayer-app/pocket-pc
Merge into: lp:mediaplayer-app
Diff against target: 558 lines (+322/-54)
9 files modified
data/CMakeLists.txt (+5/-0)
data/mediaplayer-app-content.json (+5/-0)
debian/mediaplayer-app.install (+1/-0)
src/mediaplayer.cpp (+55/-0)
src/mediaplayer.h (+1/-0)
src/qml/player.qml (+42/-16)
src/qml/player/Controls.qml (+6/-3)
src/qml/player/ToolBar.qml (+122/-0)
src/qml/player/VideoPlayer.qml (+85/-35)
To merge this branch: bzr merge lp:~renatofilho/mediaplayer-app/pocket-pc
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Needs Fixing
Ubuntu Phablet Team Pending
Review via email: mp+283299@code.launchpad.net

Commit message

*Implement support for content hub.
*Use themed icons for full screen button.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
384. By Renato Araujo Oliveira Filho

Implement support for content hub.

Copy imported files to user video dir and play the video.

385. By Renato Araujo Oliveira Filho

Does not show the empty ulr dialgo if the app was launched by content hub.

386. By Renato Araujo Oliveira Filho

Does not set the app visibility to defaul on app startup.

387. By Renato Araujo Oliveira Filho

Pause video before start sekking to avoid problems with the player.

388. By Renato Araujo Oliveira Filho

Wait the app full load before check for empty url.

Avoid problems with content hub hasPending property.

389. By Renato Araujo Oliveira Filho

Fixed wrong return for copyFiles function.

Apped the newFile path into the result list.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
390. By Renato Araujo Oliveira Filho

Check if the app was launched by content hub a bit late, to give it some time to load.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
391. By Renato Araujo Oliveira Filho

Fixed seek with media-hub.

Keep track of seek position to use in consecutives seek operation.

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)
392. By Renato Araujo Oliveira Filho

Created a toolbar component to allow us to control the hide/show animation.
Make sure that the controls are visible before start seeking.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
393. By Renato Araujo Oliveira Filho

Remove unecessary mouser area.

394. By Renato Araujo Oliveira Filho

Remove debug message.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
395. By Renato Araujo Oliveira Filho

Reduce controls timeout.
Fix control dissapearing after a seek.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
396. By Renato Araujo Oliveira Filho

Updated toolbar behaviour.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
397. By Renato Araujo Oliveira Filho

Trunk merged.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
398. By Renato Araujo Oliveira Filho

Revert changes on play/pause behavior.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'data/CMakeLists.txt'
2--- data/CMakeLists.txt 2014-10-02 13:42:17 +0000
3+++ data/CMakeLists.txt 2016-01-26 22:05:58 +0000
4@@ -2,6 +2,7 @@
5 set(MEDIAPLAYER_APP_ICON mediaplayer-app.png)
6 set(MEDIAPLAYER_APP_SYMBOLIC_ICON mediaplayer-app-symbolic.svg)
7 set(MEDIAPLAYER_URL_DISPATCHER mediaplayer-app.url-dispatcher)
8+set(MEDIAPLAYER_CONTENT_HUB mediaplayer-app-content.json)
9
10 configure_file(${DESKTOP_FILE}.in.in ${DESKTOP_FILE}.in)
11 add_custom_target(${DESKTOP_FILE} ALL
12@@ -20,3 +21,7 @@
13 install(FILES ${MEDIAPLAYER_URL_DISPATCHER}
14 DESTINATION ${CMAKE_INSTALL_DATADIR}/url-dispatcher/urls
15 )
16+
17+install(FILES ${MEDIAPLAYER_CONTENT_HUB}
18+ DESTINATION ${CMAKE_INSTALL_DATADIR}/content-hub/peers
19+ RENAME mediaplayer-app)
20
21=== added file 'data/mediaplayer-app-content.json'
22--- data/mediaplayer-app-content.json 1970-01-01 00:00:00 +0000
23+++ data/mediaplayer-app-content.json 2016-01-26 22:05:58 +0000
24@@ -0,0 +1,5 @@
25+{
26+ "destination": [
27+ "videos"
28+ ]
29+}
30
31=== modified file 'debian/mediaplayer-app.install'
32--- debian/mediaplayer-app.install 2014-09-29 20:53:44 +0000
33+++ debian/mediaplayer-app.install 2016-01-26 22:05:58 +0000
34@@ -5,3 +5,4 @@
35 /usr/share/mediaplayer-app/qml/*
36 /usr/share/locale/*/LC_MESSAGES/mediaplayer-app.mo
37 /usr/share/url-dispatcher/urls/*
38+/usr/share/content-hub/peers/mediaplayer-app
39
40=== modified file 'src/mediaplayer.cpp'
41--- src/mediaplayer.cpp 2016-01-05 15:12:20 +0000
42+++ src/mediaplayer.cpp 2016-01-26 22:05:58 +0000
43@@ -25,6 +25,7 @@
44 #include <QtCore/QLibrary>
45 #include <QtCore/QTimer>
46 #include <QtCore/QStandardPaths>
47+#include <QtCore/QMimeDatabase>
48 #include <QtWidgets/QFileDialog>
49 #include <QtQml/QQmlContext>
50 #include <QtQml/QQmlEngine>
51@@ -216,3 +217,57 @@
52
53 return fileName;
54 }
55+
56+QList<QUrl> MediaPlayer::copyFiles(const QList<QUrl> &urls)
57+{
58+ static QString moviesDir = QStandardPaths::writableLocation(QStandardPaths::MoviesLocation);
59+
60+ QList<QUrl> result;
61+
62+ Q_FOREACH(const QUrl &url, urls) {
63+ if (!url.isLocalFile()) {
64+ qWarning() << "Remote files not supported:" << url;
65+ continue;
66+ }
67+
68+ QFileInfo originalFile(url.toLocalFile());
69+
70+ QString filename = originalFile.fileName();
71+ QString suffix = originalFile.completeSuffix();
72+ QString filenameWithoutSuffix;
73+ if (suffix.isEmpty()) {
74+ QMimeDatabase mdb;
75+ QMimeType mt = mdb.mimeTypeForFile(originalFile.absoluteFilePath());
76+
77+ // If the filename doesn't have an extension add one from the
78+ // detected mimetype
79+ if(!mt.preferredSuffix().isEmpty()) {
80+ suffix = mt.preferredSuffix();
81+ }
82+ filenameWithoutSuffix = filename;
83+ } else {
84+ filenameWithoutSuffix = originalFile.baseName();
85+ }
86+
87+ QFileInfo newFile(moviesDir, QString("%1.%2").arg(filenameWithoutSuffix).arg(suffix));
88+ if (newFile.exists()) {
89+ // find a alternative name
90+ int index = 1;
91+ do {
92+ newFile = QFileInfo(moviesDir,
93+ QString("%1(%2).%3")
94+ .arg(filenameWithoutSuffix)
95+ .arg(index)
96+ .arg(suffix));
97+ index++;
98+ } while (newFile.exists());
99+ }
100+
101+ if (QFile::copy(originalFile.absoluteFilePath(), newFile.absoluteFilePath())) {
102+ result << QUrl::fromLocalFile(newFile.absoluteFilePath());
103+ } else {
104+ qWarning() << "Fail to copy file from:" << originalFile.absoluteFilePath() << "to" << newFile.absoluteFilePath();
105+ }
106+ }
107+ return result;
108+}
109
110=== modified file 'src/mediaplayer.h'
111--- src/mediaplayer.h 2014-02-13 18:26:52 +0000
112+++ src/mediaplayer.h 2016-01-26 22:05:58 +0000
113@@ -40,6 +40,7 @@
114 void onHeightChanged(int);
115 bool isDesktopMode() const;
116 QUrl chooseFile();
117+ QList<QUrl> copyFiles(const QList<QUrl> &urls);
118
119 private:
120 QQuickView *m_view;
121
122=== modified file 'src/qml/player.qml'
123--- src/qml/player.qml 2015-04-28 18:56:10 +0000
124+++ src/qml/player.qml 2016-01-26 22:05:58 +0000
125@@ -25,6 +25,7 @@
126 import Ubuntu.Unity.Action 1.1 as UnityActions
127 import Ubuntu.Components 1.1
128 import Ubuntu.Components.Popups 1.0 as Popups
129+import Ubuntu.Content 0.1 as ContentHub
130
131 Item {
132 id: mediaPlayer
133@@ -35,7 +36,6 @@
134 property string formFactor: "phone"
135 property real volume: playerLoader.item.volume
136 property bool appActive: Qt.application.active
137-
138 property variant nativeOrientation: Screen.primaryOrientation
139
140 onAppActiveChanged: {
141@@ -84,16 +84,8 @@
142 item.focus = true
143 item.rotating = Qt.binding(function () { return rotatingTransition.running } )
144 if (playUri != "") {
145+ lateUrlCheck.stop()
146 item.playUri(playUri)
147- } else {
148- if (mpApplication.desktopMode) {
149- var videoFile = mpApplication.chooseFile()
150- if (videoFile != "") {
151- item.playUri(videoFile)
152- }
153- } else {
154- PopupUtils.open(dialogNoUrl, null)
155- }
156 }
157 }
158
159@@ -212,14 +204,10 @@
160 }
161
162 Keys.onReleased: {
163- if (!event.isAutoRepeat
164- && (event.key == Qt.Key_F11 || event.key == Qt.Key_F)) {
165- event.accepted = true
166- application.toggleFullscreen();
167- } else if (!event.isAutoRepeat && event.key == Qt.Key_BracketLeft) {
168+ if (!event.isAutoRepeat && event.key === Qt.Key_BracketLeft) {
169 event.accepted = true
170 rotateClockwise()
171- } else if (!event.isAutoRepeat && event.key == Qt.Key_BracketRight) {
172+ } else if (!event.isAutoRepeat && event.key === Qt.Key_BracketRight) {
173 event.accepted = true
174 rotateCounterClockwise()
175 }
176@@ -229,9 +217,47 @@
177 target: UriHandler
178 onOpened: {
179 for (var i = 0; i < uris.length; ++i) {
180+ lateUrlCheck.stop()
181 var videoUri = uris[i].replace("video://", "file://")
182 playerLoader.item.playUri(videoUri)
183 }
184 }
185 }
186+
187+ Connections {
188+ target: ContentHub.ContentHub
189+ onImportRequested: {
190+ lateUrlCheck.stop()
191+ if (transfer.state === ContentHub.ContentTransfer.Charged) {
192+ var urls = []
193+ for(var i=0; i < transfer.items.length; i++) {
194+ urls.push(transfer.items[i].url)
195+ }
196+
197+ var result = mpApplication.copyFiles(urls);
198+ if (result.length > 0)
199+ playerLoader.item.playUri(result[result.length - 1])
200+ }
201+ }
202+ }
203+
204+ Timer {
205+ id: lateUrlCheck
206+
207+ interval: 1000
208+ repeat: false
209+ running: true
210+ onTriggered: {
211+ if ((playUri == "") && !ContentHub.ContentHub.hasPending) {
212+ if (mpApplication.desktopMode) {
213+ var videoFile = mpApplication.chooseFile()
214+ if (videoFile != "") {
215+ playerLoader.item.playUri(videoFile)
216+ }
217+ } else {
218+ PopupUtils.open(dialogNoUrl, null)
219+ }
220+ }
221+ }
222+ }
223 }
224
225=== modified file 'src/qml/player/Controls.qml'
226--- src/qml/player/Controls.qml 2015-03-20 17:34:56 +0000
227+++ src/qml/player/Controls.qml 2016-01-26 22:05:58 +0000
228@@ -19,6 +19,7 @@
229 * along with this program. If not, see <http://www.gnu.org/licenses/>.
230 */
231 import QtQuick 2.0
232+import QtQuick.Window 2.2
233 import QtMultimedia 5.0
234 import Ubuntu.Components 1.1
235
236@@ -152,13 +153,16 @@
237 id: _fullScreenButton
238
239 //TODO: use the correct icon based on window state
240- iconSource: mpApplication.desktopMode ? "artwork/icon_exitfscreen.png" : "image://theme/back"
241+ iconSource: mpApplication.desktopMode ?
242+ Window.visibility === Window.FullScreen ? "image://theme/view-restore" : "image://theme/view-fullscreen" :
243+ "image://theme/close"
244 iconSize: units.gu(3)
245 anchors.verticalCenter: parent.verticalCenter
246- width: units.gu(8)
247+ width: visible ? units.gu(8) : 0
248 height: units.gu(4)
249 onClicked: controls.fullscreenClicked()
250 leftAlignment: true
251+ visible: (mpApplication.desktopMode || (Window.visibility === Window.FullScreen))
252 }
253
254 VLine {
255@@ -341,7 +345,6 @@
256 Connections {
257 target: controls
258 onPlayerStatusChanged: {
259- console.debug("onPlayerStatusChanged")
260 _timeline.playerStatus = controls.playerStatus
261 }
262 }
263
264=== added file 'src/qml/player/ToolBar.qml'
265--- src/qml/player/ToolBar.qml 1970-01-01 00:00:00 +0000
266+++ src/qml/player/ToolBar.qml 2016-01-26 22:05:58 +0000
267@@ -0,0 +1,122 @@
268+/*
269+ * Copyright (C) 2013 Canonical, Ltd.
270+ *
271+ * Authors:
272+ * Renato Araujo Oliveira Filho <renato@canonical.com>
273+ *
274+ * This program is free software; you can redistribute it and/or modify
275+ * it under the terms of the GNU General Public License as published by
276+ * the Free Software Foundation; version 3.
277+ *
278+ * This program is distributed in the hope that it will be useful,
279+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
280+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
281+ * GNU General Public License for more details.
282+ *
283+ * You should have received a copy of the GNU General Public License
284+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
285+ */
286+
287+import QtQuick 2.0
288+import Ubuntu.Components 1.1
289+
290+
291+MouseArea {
292+ id: root
293+
294+ property bool active: false
295+ readonly property alias aboutToDismiss: dismissControls.running
296+ default property alias controls: contents.children
297+ readonly property bool fullVisible: (spacer.height === 0)
298+
299+ function dismiss()
300+ {
301+ dismissControls.restart()
302+ }
303+
304+ function abortDismiss()
305+ {
306+ dismissControls.stop()
307+ active = true
308+ }
309+
310+ onActiveChanged: dismissControls.stop()
311+
312+ hoverEnabled: true
313+ onExited: dismiss()
314+ onEntered: {
315+ abortDismiss()
316+ active = true
317+ }
318+
319+ Timer {
320+ id: dismissControls
321+
322+ running: false
323+ interval: 3000
324+ repeat: false
325+ onTriggered: root.active = false
326+ }
327+
328+ Column {
329+ anchors.fill: parent
330+ Item {
331+ id: spacer
332+ anchors {
333+ left: parent.left
334+ right: parent.right
335+ }
336+ height: root.active ? 0 : contents.height
337+ }
338+ Item {
339+ id: contents
340+ anchors {
341+ left: parent.left
342+ right: parent.right
343+ }
344+ height: childrenRect.height
345+ }
346+ }
347+
348+ states: [
349+ State {
350+ name: "active"
351+ when: root.active
352+ PropertyChanges {
353+ target: spacer
354+ height: 0
355+ enabled: false
356+ }
357+ },
358+ State {
359+ name: "deActive"
360+ when: !root.active
361+ PropertyChanges {
362+ target: spacer
363+ height: contents.height
364+ enabled: true
365+ }
366+ }
367+ ]
368+
369+ transitions: [
370+ Transition {
371+ from: "deActive"
372+ to: "active"
373+ UbuntuNumberAnimation {
374+ target: spacer
375+ property: "height"
376+ duration: UbuntuAnimation.FastDuration
377+ }
378+ },
379+ Transition {
380+ from: "active"
381+ to: "deActive"
382+ UbuntuNumberAnimation {
383+ target: spacer
384+ property: "height"
385+ duration: UbuntuAnimation.SlowDuration
386+ }
387+ }
388+ ]
389+}
390
391=== modified file 'src/qml/player/VideoPlayer.qml'
392--- src/qml/player/VideoPlayer.qml 2015-05-01 20:10:32 +0000
393+++ src/qml/player/VideoPlayer.qml 2016-01-26 22:05:58 +0000
394@@ -78,7 +78,8 @@
395 // videoOutput: player.videoOutput
396 // }
397
398- GenericToolbar {
399+
400+ ToolBar {
401 id: _controls
402
403 objectName: "toolbar"
404@@ -89,11 +90,48 @@
405 }
406
407 height: _controlsContents.height
408-
409 Controls {
410 id: _controlsContents
411
412- property bool isPaused: false
413+ property bool wasPausedBeforeSeek: false
414+ property bool wasVisibleBeforeSeek: false
415+ property int seekPosition: 0
416+
417+ function aboutToSeek()
418+ {
419+ wasPausedBeforeSeek = (state == "paused")
420+ wasVisibleBeforeSeek = _controls.active && !_controls.aboutToDismiss
421+ _controls.abortDismiss()
422+ player.pause()
423+ _controls.active = true
424+ _controlsContents.seekPosition = video.position
425+ }
426+
427+ function seekDone()
428+ {
429+ // Only automatically resume playing after a seek that is not to the
430+ // end of stream (i.e. position == duration)
431+ if (player.status !== MediaPlayer.EndOfMedia && !_controlsContents.wasPausedBeforeSeek) {
432+ player.play()
433+ }
434+
435+ if (!_controlsContents.wasVisibleBeforeSeek) {
436+ _controls.dismiss()
437+ }
438+
439+ _controlsContents.seekPosition = -1
440+ _controlsContents.wasPausedBeforeSeek = false
441+ _controlsContents.wasVisibleBeforeSeek = false
442+ }
443+
444+ function seek(time)
445+ {
446+ //keep trak of last seek position in case of the last seek does not complete in time
447+ //sometimes the seek is too fast and we can not rely on the video position to calculate
448+ //the next seek position.
449+ _controlsContents.seekPosition = time
450+ player.video.seek(time)
451+ }
452
453 settingsEnabled: mpApplication.desktopMode
454
455@@ -120,22 +158,9 @@
456 }
457 }
458
459- onSeekRequested: {
460- player.video.seek(time)
461- }
462-
463- onStartSeek: {
464- isPaused = (state == "paused")
465- player.pause()
466- }
467-
468- onEndSeek: {
469- // Only automatically resume playing after a seek that is not to the
470- // end of stream (i.e. position == duration)
471- if (player.status != MediaPlayer.EndOfMedia && !isPaused) {
472- player.play()
473- }
474- }
475+ onStartSeek: aboutToSeek()
476+ onEndSeek: seekDone()
477+ onSeekRequested: seek(time)
478
479 onSettingsClicked: {
480 if (mpApplication.desktopMode) {
481@@ -150,17 +175,34 @@
482 }
483
484 MouseArea {
485- id: _mouseArea
486-
487- objectName: "videoMouseArea"
488- anchors {
489- left: parent.left
490- right: parent.right
491- top: parent.top
492- bottom: _controls.top
493- }
494-
495- onClicked: _controls.active = !_controls.active
496+ id: _mouseArea
497+
498+ objectName: "videoMouseArea"
499+ anchors {
500+ left: parent.left
501+ right: parent.right
502+ top: parent.top
503+ bottom: _controls.top
504+ }
505+
506+ onClicked: _controls.active = !_controls.active
507+ }
508+
509+
510+ Keys.onReleased:
511+ {
512+ if (event.isAutoRepeat) {
513+ return
514+ }
515+
516+ switch(event.key) {
517+ case Qt.Key_Right:
518+ case Qt.Key_Left:
519+ _controlsContents.seekDone()
520+ break;
521+ default:
522+ break
523+ }
524 }
525
526 Keys.onPressed: {
527@@ -171,9 +213,17 @@
528 case Qt.Key_Right:
529 case Qt.Key_Left:
530 {
531- var currentPos = (video ? video.position : 0)
532- var nextPos = currentPos
533- if (event.key == Qt.Key_Right) {
534+ if (!event.isAutoRepeat) {
535+ _controlsContents.aboutToSeek()
536+ }
537+ // wait controls be fully visbile
538+ if (!_controls.fullVisible)
539+ return
540+
541+ var nextPos = _controlsContents.seekPosition >= 0 ?
542+ _controlsContents.seekPosition : 0
543+
544+ if (event.key === Qt.Key_Right) {
545 var maxPos = (video ? video.duration : 0)
546 nextPos += player.seekStep
547 if (nextPos > maxPos) {
548@@ -186,8 +236,8 @@
549 }
550 }
551
552- if (nextPos != -1) {
553- player.video.seek(nextPos)
554+ if (nextPos !== -1) {
555+ _controlsContents.seek(nextPos)
556 }
557 break;
558 }

Subscribers

People subscribed via source and target branches