Merge lp:~uriboni/camera-app/content-hub-video-source into lp:camera-app/staging

Proposed by Ugo Riboni
Status: Merged
Approved by: Florian Boucault
Approved revision: 624
Merged at revision: 622
Proposed branch: lp:~uriboni/camera-app/content-hub-video-source
Merge into: lp:camera-app/staging
Diff against target: 400 lines (+173/-71)
6 files modified
VideoReview.qml (+63/-0)
ViewFinderExportConfirmation.qml (+66/-49)
ViewFinderOverlay.qml (+3/-1)
ViewFinderView.qml (+28/-16)
camera-app.qml (+11/-4)
camera-contenthub.json (+2/-1)
To merge this branch: bzr merge lp:~uriboni/camera-app/content-hub-video-source
Reviewer Review Type Date Requested Status
Florian Boucault (community) Approve
Review via email: mp+280140@code.launchpad.net

Commit message

Enable camera-app as source for videos for content-hub export, and adjust the UI

Description of the change

Enable camera-app as source for videos for content-hub export, and adjust the UI

To post a comment you must log in.
623. By Ugo Riboni

Merge changes from staging

624. By Ugo Riboni

Cleanup

Revision history for this message
Florian Boucault (fboucault) wrote :

It's all good in fact. Well done

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'VideoReview.qml'
--- VideoReview.qml 1970-01-01 00:00:00 +0000
+++ VideoReview.qml 2015-12-10 12:37:24 +0000
@@ -0,0 +1,63 @@
1/*
2 * Copyright 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
17import QtQuick 2.4
18import Ubuntu.Components 1.3
19
20Item {
21 property string videoPath
22 property int bottomMargin
23
24 Image {
25 id: thumbnail
26 anchors.fill: parent
27
28 fillMode: Image.PreserveAspectFit
29 sourceSize.width: width
30 sourceSize.height: height
31
32 source: videoPath ? "image://thumbnailer/%1".arg(videoPath) : ""
33 opacity: status == Image.Ready ? 1.0 : 0.0
34 Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration } }
35 }
36
37 Item {
38 anchors.fill: parent
39 anchors.bottomMargin: parent.bottomMargin
40
41 ActivityIndicator {
42 anchors.centerIn: parent
43 visible: running
44 running: thumbnail.status != Image.Ready
45 }
46
47 Icon {
48 width: units.gu(5)
49 height: units.gu(5)
50 anchors.centerIn: parent
51 name: "media-playback-start"
52 color: "white"
53 opacity: 0.8
54 }
55
56 MouseArea {
57 anchors.centerIn: parent
58 width: units.gu(10)
59 height: units.gu(10)
60 onClicked: Qt.openUrlExternally("video://%1".arg(videoPath));
61 }
62 }
63}
064
=== modified file 'ViewFinderExportConfirmation.qml'
--- ViewFinderExportConfirmation.qml 2015-10-22 12:21:14 +0000
+++ ViewFinderExportConfirmation.qml 2015-12-10 12:37:24 +0000
@@ -20,6 +20,7 @@
20Item {20Item {
21 id: viewFinderExportConfirmation21 id: viewFinderExportConfirmation
2222
23 property bool isVideo
23 property string mediaPath24 property string mediaPath
24 property Snapshot snapshot25 property Snapshot snapshot
2526
@@ -27,7 +28,7 @@
27 viewFinder.visible = false;28 viewFinder.visible = false;
28 viewFinderOverlay.visible = false;29 viewFinderOverlay.visible = false;
29 mediaPath = path;30 mediaPath = path;
30 snapshot.visible = true;31 if (!isVideo) snapshot.visible = true;
31 visible = true;32 visible = true;
32 }33 }
3334
@@ -46,54 +47,70 @@
46 asynchronous: true47 asynchronous: true
47 sourceComponent: Component {48 sourceComponent: Component {
48 Item {49 Item {
49 CircleButton {50 VideoReview {
50 id: retryButton51 id: videoReview
51 objectName: "retryButton"52 anchors.fill: parent
5253 bottomMargin: buttons.height
53 anchors {54 videoPath: mediaPath
54 right: validateButton.left55 visible: isVideo
55 rightMargin: units.gu(7.5)56 }
56 bottom: parent.bottom57
57 bottomMargin: units.gu(6)58 Item {
58 }59 id: buttons
5960 anchors.bottom: parent.bottom
60 iconName: "reload"61 anchors.left: parent.left
61 onClicked: viewFinderExportConfirmation.hide()62 anchors.right: parent.right
62 }63 height: childrenRect.height
6364
64 CircleButton {65 CircleButton {
65 id: validateButton66 id: retryButton
66 objectName: "validateButton"67 objectName: "retryButton"
6768
68 width: units.gu(8)69 anchors {
69 anchors {70 right: validateButton.left
70 bottom: parent.bottom71 rightMargin: units.gu(7.5)
71 bottomMargin: units.gu(5)72 bottom: parent.bottom
72 horizontalCenter: parent.horizontalCenter73 bottomMargin: units.gu(6)
73 }74 }
7475
75 iconName: "ok"76 iconName: "reload"
76 onClicked: {77 onClicked: viewFinderExportConfirmation.hide()
77 viewFinderExportConfirmation.hide();78 }
78 main.exportContent([mediaPath]);79
79 }80 CircleButton {
80 }81 id: validateButton
8182 objectName: "validateButton"
82 CircleButton {83
83 id: cancelButton84 width: units.gu(8)
84 objectName: "cancelButton"85 anchors {
8586 bottom: parent.bottom
86 anchors {87 bottomMargin: units.gu(5)
87 left: validateButton.right88 horizontalCenter: parent.horizontalCenter
88 leftMargin: units.gu(7.5)89 }
89 bottom: parent.bottom90
90 bottomMargin: units.gu(6)91 iconName: "ok"
91 }92 onClicked: {
9293 viewFinderExportConfirmation.hide();
93 iconName: "close"94 main.exportContent([mediaPath]);
94 onClicked: {95 }
95 viewFinderExportConfirmation.hide();96 }
96 main.cancelExport();97
98 CircleButton {
99 id: cancelButton
100 objectName: "cancelButton"
101
102 anchors {
103 left: validateButton.right
104 leftMargin: units.gu(7.5)
105 bottom: parent.bottom
106 bottomMargin: units.gu(6)
107 }
108
109 iconName: "close"
110 onClicked: {
111 viewFinderExportConfirmation.hide();
112 main.cancelExport();
113 }
97 }114 }
98 }115 }
99 }116 }
100117
=== modified file 'ViewFinderOverlay.qml'
--- ViewFinderOverlay.qml 2015-12-08 15:14:04 +0000
+++ ViewFinderOverlay.qml 2015-12-10 12:37:24 +0000
@@ -667,7 +667,9 @@
667 }667 }
668668
669 if (camera.captureMode == Camera.CaptureVideo) {669 if (camera.captureMode == Camera.CaptureVideo) {
670 if (application.removableStoragePresent && settings.preferRemovableStorage) {670 if (main.contentExportMode) {
671 camera.videoRecorder.outputLocation = application.temporaryLocation;
672 } else if (application.removableStoragePresent && settings.preferRemovableStorage) {
671 camera.videoRecorder.outputLocation = application.removableStorageVideosLocation;673 camera.videoRecorder.outputLocation = application.removableStorageVideosLocation;
672 } else {674 } else {
673 camera.videoRecorder.outputLocation = application.videosLocation;675 camera.videoRecorder.outputLocation = application.videosLocation;
674676
=== modified file 'ViewFinderView.qml'
--- ViewFinderView.qml 2015-12-07 15:04:16 +0000
+++ ViewFinderView.qml 2015-12-10 12:37:24 +0000
@@ -20,6 +20,7 @@
20import QtMultimedia 5.020import QtMultimedia 5.0
21import CameraApp 0.121import CameraApp 0.1
22import QtGraphicalEffects 1.022import QtGraphicalEffects 1.0
23import Ubuntu.Content 0.1
2324
24Item {25Item {
25 id: viewFinderView26 id: viewFinderView
@@ -33,7 +34,6 @@
33 signal photoTaken(string filePath)34 signal photoTaken(string filePath)
34 signal videoShot(string filePath)35 signal videoShot(string filePath)
3536
36
37 onInViewChanged: decideCameraState()37 onInViewChanged: decideCameraState()
38 Connections {38 Connections {
39 target: viewFinderOverlay39 target: viewFinderOverlay
@@ -58,7 +58,7 @@
58 }58 }
59 }59 }
6060
61 Camera {61 property Camera camera: Camera {
62 id: camera62 id: camera
63 captureMode: Camera.CaptureStillImage63 captureMode: Camera.CaptureStillImage
64 cameraState: Camera.UnloadedState64 cameraState: Camera.UnloadedState
@@ -100,8 +100,17 @@
100 property alias currentZoom: camera.digitalZoom100 property alias currentZoom: camera.digitalZoom
101 property alias maximumZoom: camera.maximumDigitalZoom101 property alias maximumZoom: camera.maximumDigitalZoom
102 property bool switchInProgress: false102 property bool switchInProgress: false
103 103
104 imageCapture {104 imageCapture {
105 onReadyChanged: {
106 if (camera.imageCapture.ready && main.transfer) {
107 if (main.transfer.contentType === ContentType.Videos) {
108 viewFinderView.captureMode = Camera.CaptureVideo;
109 } else {
110 viewFinderView.captureMode = Camera.CaptureStillImage;
111 }
112 }
113 }
105 onCaptureFailed: {114 onCaptureFailed: {
106 console.log("Capture failed for request " + requestId + ": " + message);115 console.log("Capture failed for request " + requestId + ": " + message);
107 }116 }
@@ -124,16 +133,18 @@
124 console.log("Picture saved as " + path);133 console.log("Picture saved as " + path);
125 }134 }
126 }135 }
127 136
128 videoRecorder {137 videoRecorder {
129 onRecorderStateChanged: {138 onRecorderStateChanged: {
130 if (videoRecorder.recorderState === CameraRecorder.StoppedState) {139 if (videoRecorder.recorderState === CameraRecorder.StoppedState) {
131 if (photoRollHint.necessary) {
132 photoRollHint.enable();
133 }
134 metricVideos.increment()140 metricVideos.increment()
135 viewFinderOverlay.visible = true;141 viewFinderOverlay.visible = true;
136 viewFinderView.videoShot(videoRecorder.actualLocation);142 viewFinderView.videoShot(videoRecorder.actualLocation);
143 if (main.contentExportMode) {
144 viewFinderExportConfirmation.confirmExport(videoRecorder.actualLocation);
145 } else if (photoRollHint.necessary) {
146 photoRollHint.enable();
147 }
137 }148 }
138 }149 }
139 }150 }
@@ -168,7 +179,7 @@
168 angle: 180179 angle: 180
169 }180 }
170 }181 }
171 182
172 transform: [183 transform: [
173 Scale {184 Scale {
174 id: viewFinderSwitcherScale185 id: viewFinderSwitcherScale
@@ -185,11 +196,11 @@
185 angle: 0196 angle: 0
186 }197 }
187 ]198 ]
188 199
189 200
190 SequentialAnimation {201 SequentialAnimation {
191 id: viewFinderSwitcherAnimation202 id: viewFinderSwitcherAnimation
192 203
193 SequentialAnimation {204 SequentialAnimation {
194 ParallelAnimation {205 ParallelAnimation {
195 UbuntuNumberAnimation {target: viewFinderSwitcherScale; property: "xScale"; from: 1.0; to: 0.8; duration: UbuntuAnimation.BriskDuration ; easing: UbuntuAnimation.StandardEasing}206 UbuntuNumberAnimation {target: viewFinderSwitcherScale; property: "xScale"; from: 1.0; to: 0.8; duration: UbuntuAnimation.BriskDuration ; easing: UbuntuAnimation.StandardEasing}
@@ -218,16 +229,16 @@
218 }229 }
219 }230 }
220 }231 }
221 232
222 VideoOutput {233 VideoOutput {
223 id: viewFinder234 id: viewFinder
224 235
225 x: 0236 x: 0
226 y: -viewFinderGeometry.y237 y: -viewFinderGeometry.y
227 width: parent.width238 width: parent.width
228 height: parent.height239 height: parent.height
229 source: camera240 source: camera
230 241
231 /* This rotation need to be applied since the camera hardware in the242 /* This rotation need to be applied since the camera hardware in the
232 Galaxy Nexus phone is mounted at an angle inside the device, so the video243 Galaxy Nexus phone is mounted at an angle inside the device, so the video
233 feed is rotated too.244 feed is rotated too.
@@ -245,7 +256,7 @@
245 // may change.256 // may change.
246 orientation = Screen.primaryOrientation === Qt.PortraitOrientation ? -90 : 0;257 orientation = Screen.primaryOrientation === Qt.PortraitOrientation ? -90 : 0;
247 }258 }
248 259
249 /* Convenience item tracking the real position and size of the real video feed.260 /* Convenience item tracking the real position and size of the real video feed.
250 Having this helps since these values depend on a lot of rules:261 Having this helps since these values depend on a lot of rules:
251 - the feed is automatically scaled to fit the viewfinder262 - the feed is automatically scaled to fit the viewfinder
@@ -253,7 +264,7 @@
253 - the resolution and aspect ratio of the feed changes depending on the active camera264 - the resolution and aspect ratio of the feed changes depending on the active camera
254 The item is also separated in a component so it can be unit tested.265 The item is also separated in a component so it can be unit tested.
255 */266 */
256 267
257 transform: Rotation {268 transform: Rotation {
258 origin.x: viewFinder.width / 2269 origin.x: viewFinder.width / 2
259 origin.y: viewFinder.height / 2270 origin.y: viewFinder.height / 2
@@ -434,5 +445,6 @@
434 id: viewFinderExportConfirmation445 id: viewFinderExportConfirmation
435 anchors.fill: parent446 anchors.fill: parent
436 snapshot: snapshot447 snapshot: snapshot
448 isVideo: main.transfer.contentType == ContentType.Videos
437 }449 }
438}450}
439451
=== modified file 'camera-app.qml'
--- camera-app.qml 2015-11-20 15:36:00 +0000
+++ camera-app.qml 2015-12-10 12:37:24 +0000
@@ -311,10 +311,17 @@
311 target: ContentHub311 target: ContentHub
312 onExportRequested: {312 onExportRequested: {
313 viewSwitcher.switchToViewFinder();313 viewSwitcher.switchToViewFinder();
314 if (transferContentType === ContentType.Videos) {314
315 viewFinderView.captureMode = Camera.CaptureVideo;315 // The exportRequested event can arrive before or after the
316 } else {316 // app is active, but setting the recording type before the
317 viewFinderView.captureMode = Camera.CaptureStillImage;317 // capture becomes ready does not have any effect.
318 // See camera.imageCapture.onReadyChanged for the other case.
319 if (viewFinderView.camera.imageCapture.ready) {
320 if (transfer.contentType === ContentType.Videos) {
321 viewFinderView.captureMode = Camera.CaptureVideo;
322 } else {
323 viewFinderView.captureMode = Camera.CaptureStillImage;
324 }
318 }325 }
319 main.transfer = transfer;326 main.transfer = transfer;
320 }327 }
321328
=== modified file 'camera-contenthub.json'
--- camera-contenthub.json 2014-07-31 18:41:17 +0000
+++ camera-contenthub.json 2015-12-10 12:37:24 +0000
@@ -1,5 +1,6 @@
1{1{
2 "source": [2 "source": [
3 "pictures"3 "pictures",
4 "videos"
4 ]5 ]
5}6}

Subscribers

People subscribed via source and target branches