Merge lp:~fboucault/camera-app/custom_rotation into lp:camera-app

Proposed by Florian Boucault
Status: Merged
Approved by: Bill Filler
Approved revision: 573
Merged at revision: 571
Proposed branch: lp:~fboucault/camera-app/custom_rotation
Merge into: lp:camera-app
Prerequisite: lp:~canonical-platform-qa/camera-app/fix1444170-flake8
Diff against target: 1040 lines (+478/-365)
10 files modified
CircleButton.qml (+3/-2)
OptionButton.qml (+1/-0)
OptionsOverlay.qml (+1/-1)
PhotoRollHint.qml (+30/-28)
Snapshot.qml (+20/-5)
ViewFinderOverlay.qml (+287/-279)
ViewFinderView.qml (+1/-1)
camera-app.desktop.in.in (+1/-0)
camera-app.qml (+126/-43)
tests/autopilot/camera_app/tests/test_flash.py (+8/-6)
To merge this branch: bzr merge lp:~fboucault/camera-app/custom_rotation
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Needs Fixing
Ubuntu Phablet Team Pending
Review via email: mp+263778@code.launchpad.net

This proposal supersedes a proposal from 2015-06-29.

Commit message

Set desktop file flags informing Unity that the camera app is doing its own content rotation when the device rotates.
Manually rotate/position depending on device orientation:
- photo roll
- bottom edge indicators and content
- first photo roll hint
- snapshot animation
- countdown timer

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
571. By Florian Boucault

Merged lp:~canonical-platform-qa/camera-app/fix1444170-flake8

572. By Florian Boucault

More robust camera_app.tests.test_flash.TestCameraFlash.test_flash_hdr_mutually_exclusive

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

More appropriate bottom edge input area.

Revision history for this message
Bill Filler (bfiller) wrote :

tested, works on arale and krillin

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
=== modified file 'CircleButton.qml'
--- CircleButton.qml 2014-12-05 18:57:32 +0000
+++ CircleButton.qml 2015-07-03 18:21:53 +0000
@@ -25,6 +25,7 @@
25 property url iconSource25 property url iconSource
26 property bool on: true26 property bool on: true
27 property string label: ""27 property string label: ""
28 property bool automaticOrientation: true
2829
29 width: units.gu(5)30 width: units.gu(5)
30 height: width31 height: width
@@ -49,7 +50,7 @@
49 color: "white"50 color: "white"
50 opacity: button.on ? (button.enabled ? 1.0 : 0.3): 0.551 opacity: button.on ? (button.enabled ? 1.0 : 0.3): 0.5
51 visible: label === ""52 visible: label === ""
52 rotation: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)53 rotation: automaticOrientation ? Screen.angleBetween(Screen.primaryOrientation, Screen.orientation) : 0
53 Behavior on rotation {54 Behavior on rotation {
54 RotationAnimator {55 RotationAnimator {
55 duration: UbuntuAnimation.BriskDuration56 duration: UbuntuAnimation.BriskDuration
@@ -69,7 +70,7 @@
69 text: label70 text: label
70 opacity: button.on ? (button.enabled ? 1.0 : 0.3): 0.571 opacity: button.on ? (button.enabled ? 1.0 : 0.3): 0.5
71 visible: label !== ""72 visible: label !== ""
72 rotation: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)73 rotation: automaticOrientation ? Screen.angleBetween(Screen.primaryOrientation, Screen.orientation) : 0
73 Behavior on rotation {74 Behavior on rotation {
74 RotationAnimator {75 RotationAnimator {
75 duration: UbuntuAnimation.BriskDuration76 duration: UbuntuAnimation.BriskDuration
7677
=== modified file 'OptionButton.qml'
--- OptionButton.qml 2015-01-27 11:32:45 +0000
+++ OptionButton.qml 2015-07-03 18:21:53 +0000
@@ -29,4 +29,5 @@
29 enabled: model.available29 enabled: model.available
30 label: model.label30 label: model.label
31 visible: model.visible31 visible: model.visible
32 automaticOrientation: false
32}33}
3334
=== modified file 'OptionsOverlay.qml'
--- OptionsOverlay.qml 2015-01-27 15:22:59 +0000
+++ OptionsOverlay.qml 2015-07-03 18:21:53 +0000
@@ -37,7 +37,7 @@
3737
38 columns: 338 columns: 3
39 columnSpacing: units.gu(9.5)39 columnSpacing: units.gu(9.5)
40 rowSpacing: units.gu(4)40 rowSpacing: units.gu(3)
4141
42 Repeater {42 Repeater {
43 model: optionsOverlay.options43 model: optionsOverlay.options
4444
=== modified file 'PhotoRollHint.qml'
--- PhotoRollHint.qml 2014-11-05 10:17:23 +0000
+++ PhotoRollHint.qml 2015-07-03 18:21:53 +0000
@@ -40,33 +40,35 @@
40 property alias photoRollHintNecessary: photoRollHint.necessary40 property alias photoRollHintNecessary: photoRollHint.necessary
41 }41 }
4242
43 Image {43 OrientationHelper {
44 id: hintPictogram44 Image {
45 anchors {45 id: hintPictogram
46 horizontalCenter: parent.horizontalCenter46 anchors {
47 horizontalCenterOffset: units.gu(4)47 horizontalCenter: parent.horizontalCenter
48 verticalCenter: parent.verticalCenter48 horizontalCenterOffset: units.gu(4)
49 verticalCenterOffset: -units.gu(1)49 verticalCenter: parent.verticalCenter
50 }50 verticalCenterOffset: -units.gu(1)
5151 }
52 asynchronous: true52
53 cache: false53 asynchronous: true
54 source: photoRollHint.enabled ? "assets/camera_swipe.png" : ""54 cache: false
55 }55 source: photoRollHint.enabled ? "assets/camera_swipe.png" : ""
5656 }
57 Label {57
58 id: hintLabel58 Label {
5959 id: hintLabel
60 anchors {60
61 top: hintPictogram.bottom61 anchors {
62 topMargin: units.gu(5)62 top: hintPictogram.bottom
63 horizontalCenter: parent.horizontalCenter63 topMargin: units.gu(5)
64 }64 horizontalCenter: parent.horizontalCenter
65 width: parent.width - 2 * units.gu(2)65 }
66 horizontalAlignment: Text.AlignHCenter66 width: parent.width - 2 * units.gu(2)
67 wrapMode: Text.Wrap67 horizontalAlignment: Text.AlignHCenter
68 text: i18n.tr("Swipe left for photo roll")68 wrapMode: Text.Wrap
69 fontSize: "x-large"69 text: i18n.tr("Swipe left for photo roll")
70 color: "#ebebeb"70 fontSize: "x-large"
71 color: "#ebebeb"
72 }
71 }73 }
72}74}
7375
=== modified file 'Snapshot.qml'
--- Snapshot.qml 2014-10-09 19:07:35 +0000
+++ Snapshot.qml 2015-07-03 18:21:53 +0000
@@ -15,6 +15,7 @@
15 */15 */
1616
17import QtQuick 2.217import QtQuick 2.2
18import QtQuick.Window 2.0
18import Ubuntu.Components 1.019import Ubuntu.Components 1.0
1920
20Item {21Item {
@@ -34,11 +35,8 @@
3435
35 Item {36 Item {
36 id: container37 id: container
37 anchors {
38 top: parent.top
39 bottom: parent.bottom
40 }
41 width: parent.width38 width: parent.width
39 height: parent.height
4240
43 Image {41 Image {
44 id: snapshot42 id: snapshot
@@ -69,15 +67,32 @@
69 cache: false67 cache: false
70 }68 }
71 }69 }
70 property int orientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
71 property var angleToOrientation: {0: "PORTRAIT",
72 90: "LANDSCAPE",
73 270: "INVERTED_LANDSCAPE"}
7274
73 SequentialAnimation {75 SequentialAnimation {
74 id: shoot76 id: shoot
7577
76 PropertyAction { target: snapshotRoot; property: "visible"; value: true }78 PropertyAction { target: snapshotRoot; property: "visible"; value: true }
77 PauseAnimation { duration: 150 }79 PauseAnimation { duration: 150 }
78 XAnimator { target: container; to: container.width + shadow.width; duration: UbuntuAnimation.BriskDuration; easing: UbuntuAnimation.StandardEasing}80 XAnimator {
81 target: container
82 to: angleToOrientation[orientationAngle] == "PORTRAIT" ? container.width + shadow.width : 0
83 duration: UbuntuAnimation.BriskDuration
84 easing: UbuntuAnimation.StandardEasing
85 }
86 YAnimator {
87 target: container
88 to: angleToOrientation[orientationAngle] == "LANDSCAPE" ? container.height + shadow.width :
89 angleToOrientation[orientationAngle] == "INVERTED_LANDSCAPE" ? -(container.height + shadow.width) : 0
90 duration: UbuntuAnimation.BriskDuration
91 easing: UbuntuAnimation.StandardEasing
92 }
79 PropertyAction { target: snapshot; property: "source"; value: ""}93 PropertyAction { target: snapshot; property: "source"; value: ""}
80 PropertyAction { target: snapshotRoot; property: "visible"; value: false }94 PropertyAction { target: snapshotRoot; property: "visible"; value: false }
81 PropertyAction { target: container; property: "x"; value: 0 }95 PropertyAction { target: container; property: "x"; value: 0 }
96 PropertyAction { target: container; property: "y"; value: 0 }
82 }97 }
83}98}
8499
=== modified file 'ViewFinderOverlay.qml'
--- ViewFinderOverlay.qml 2015-03-13 00:41:54 +0000
+++ ViewFinderOverlay.qml 2015-07-03 18:21:53 +0000
@@ -159,269 +159,290 @@
159 onClicked: optionsOverlayClose()159 onClicked: optionsOverlayClose()
160 }160 }
161161
162 Panel {162 OrientationHelper {
163 id: bottomEdge163 id: bottomEdgeOrientation
164 anchors {164 transitionEnabled: bottomEdge.opened
165 right: parent.right165
166 left: parent.left166 Panel {
167 bottom: parent.bottom167 id: bottomEdge
168 }
169 height: optionsOverlayLoader.height
170 onOpenedChanged: optionsOverlayLoader.item.closeValueSelector()
171 enabled: camera.videoRecorder.recorderState == CameraRecorder.StoppedState
172 opacity: enabled ? 1.0 : 0.3
173
174 Item {
175 /* Use the 'trigger' feature of Panel so that tapping on the Panel
176 has the same effect as tapping outside of it (bottomEdgeClose) */
177 id: clickReceiver
178 anchors.fill: parent
179 function trigger() {
180 optionsOverlayClose();
181 }
182 }
183
184 property real progress: (bottomEdge.height - bottomEdge.position) / bottomEdge.height
185 property list<ListModel> options: [
186 ListModel {
187 id: gpsOptionsModel
188
189 property string settingsProperty: "gpsEnabled"
190 property string icon: "location"
191 property string label: ""
192 property bool isToggle: true
193 property int selectedIndex: bottomEdge.indexForValue(gpsOptionsModel, settings.gpsEnabled)
194 property bool available: true
195 property bool visible: true
196 property bool showInIndicators: true
197
198 ListElement {
199 icon: ""
200 label: QT_TR_NOOP("On")
201 value: true
202 }
203 ListElement {
204 icon: ""
205 label: QT_TR_NOOP("Off")
206 value: false
207 }
208 },
209 ListModel {
210 id: flashOptionsModel
211
212 property string settingsProperty: "flashMode"
213 property string icon: ""
214 property string label: ""
215 property bool isToggle: false
216 property int selectedIndex: bottomEdge.indexForValue(flashOptionsModel, settings.flashMode)
217 property bool available: camera.advanced.hasFlash
218 property bool visible: camera.captureMode == Camera.CaptureStillImage
219 property bool showInIndicators: true
220
221 ListElement {
222 icon: "flash-on"
223 label: QT_TR_NOOP("On")
224 value: Camera.FlashOn
225 }
226 ListElement {
227 icon: "flash-auto"
228 label: QT_TR_NOOP("Auto")
229 value: Camera.FlashAuto
230 }
231 ListElement {
232 icon: "flash-off"
233 label: QT_TR_NOOP("Off")
234 value: Camera.FlashOff
235 }
236 },
237 ListModel {
238 id: videoFlashOptionsModel
239
240 property string settingsProperty: "videoFlashMode"
241 property string icon: ""
242 property string label: ""
243 property bool isToggle: false
244 property int selectedIndex: bottomEdge.indexForValue(videoFlashOptionsModel, settings.videoFlashMode)
245 property bool available: camera.advanced.hasFlash
246 property bool visible: camera.captureMode == Camera.CaptureVideo
247 property bool showInIndicators: true
248
249 ListElement {
250 icon: "torch-on"
251 label: QT_TR_NOOP("On")
252 value: Camera.FlashVideoLight
253 }
254 ListElement {
255 icon: "torch-off"
256 label: QT_TR_NOOP("Off")
257 value: Camera.FlashOff
258 }
259 },
260 ListModel {
261 id: hdrOptionsModel
262
263 property string settingsProperty: "hdrEnabled"
264 property string icon: ""
265 property string label: i18n.tr("HDR")
266 property bool isToggle: true
267 property int selectedIndex: bottomEdge.indexForValue(hdrOptionsModel, settings.hdrEnabled)
268 property bool available: camera.advanced.hasHdr
269 property bool visible: true
270 property bool showInIndicators: true
271
272 ListElement {
273 icon: ""
274 label: QT_TR_NOOP("On")
275 value: true
276 }
277 ListElement {
278 icon: ""
279 label: QT_TR_NOOP("Off")
280 value: false
281 }
282 },
283 ListModel {
284 id: selfTimerOptionsModel
285
286 property string settingsProperty: "selfTimerDelay"
287 property string icon: ""
288 property string iconSource: "assets/self_timer.svg"
289 property string label: ""
290 property bool isToggle: true
291 property int selectedIndex: bottomEdge.indexForValue(selfTimerOptionsModel, settings.selfTimerDelay)
292 property bool available: true
293 property bool visible: true
294 property bool showInIndicators: true
295
296 ListElement {
297 icon: ""
298 label: QT_TR_NOOP("Off")
299 value: 0
300 }
301 ListElement {
302 icon: ""
303 label: QT_TR_NOOP("5 seconds")
304 value: 5
305 }
306 ListElement {
307 icon: ""
308 label: QT_TR_NOOP("15 seconds")
309 value: 15
310 }
311 },
312 ListModel {
313 id: encodingQualityOptionsModel
314
315 property string settingsProperty: "encodingQuality"
316 property string icon: "stock_image"
317 property string label: ""
318 property bool isToggle: false
319 property int selectedIndex: bottomEdge.indexForValue(encodingQualityOptionsModel, settings.encodingQuality)
320 property bool available: true
321 property bool visible: camera.captureMode == Camera.CaptureStillImage
322 property bool showInIndicators: false
323
324 ListElement {
325 label: QT_TR_NOOP("Fine Quality")
326 value: 4 // QMultimedia.VeryHighQuality
327 }
328 ListElement {
329 label: QT_TR_NOOP("Normal Quality")
330 value: 2 // QMultimedia.NormalQuality
331 }
332 ListElement {
333 label: QT_TR_NOOP("Basic Quality")
334 value: 1 // QMultimedia.LowQuality
335 }
336 },
337 ListModel {
338 id: gridOptionsModel
339
340 property string settingsProperty: "gridEnabled"
341 property string icon: ""
342 property string iconSource: "assets/grid_lines.svg"
343 property string label: ""
344 property bool isToggle: true
345 property int selectedIndex: bottomEdge.indexForValue(gridOptionsModel, settings.gridEnabled)
346 property bool available: true
347 property bool visible: true
348
349 ListElement {
350 icon: ""
351 label: QT_TR_NOOP("On")
352 value: true
353 }
354 ListElement {
355 icon: ""
356 label: QT_TR_NOOP("Off")
357 value: false
358 }
359 },
360 ListModel {
361 id: removableStorageOptionsModel
362
363 property string settingsProperty: "preferRemovableStorage"
364 property string icon: ""
365 property string label: i18n.tr("SD")
366 property bool isToggle: true
367 property int selectedIndex: bottomEdge.indexForValue(removableStorageOptionsModel, settings.preferRemovableStorage)
368 property bool available: application.removableStoragePresent
369 property bool visible: available
370
371 ListElement {
372 icon: ""
373 label: QT_TR_NOOP("Save to SD Card")
374 value: true
375 }
376 ListElement {
377 icon: ""
378 label: QT_TR_NOOP("Save internally")
379 value: false
380 }
381 },
382 ListModel {
383 id: videoResolutionOptionsModel
384
385 property string settingsProperty: "videoResolution"
386 property string icon: ""
387 property string label: "HD"
388 property bool isToggle: false
389 property int selectedIndex: bottomEdge.indexForValue(videoResolutionOptionsModel, settings.videoResolution)
390 property bool available: true
391 property bool visible: camera.captureMode == Camera.CaptureVideo
392 property bool showInIndicators: false
393 }
394 ]
395
396 /* FIXME: application.removableStoragePresent is not updated dynamically.
397 Workaround that by reading it when the bottom edge is opened/closed.
398 */
399 Connections {
400 target: bottomEdge
401 onOpenedChanged: removableStorageOptionsModel.available = application.removableStoragePresent
402 }
403
404 function indexForValue(model, value) {
405 var i;
406 var element;
407 for (i=0; i<model.count; i++) {
408 element = model.get(i);
409 if (element.value === value) {
410 return i;
411 }
412 }
413
414 return -1;
415 }
416
417 BottomEdgeIndicators {
418 options: bottomEdge.options
419 anchors {168 anchors {
420 horizontalCenter: parent.horizontalCenter169 right: parent.right
421 bottom: parent.top170 left: parent.left
422 }171 bottom: parent.bottom
423 opacity: bottomEdge.pressed || bottomEdge.opened ? 0.0 : 1.0172 }
424 Behavior on opacity { UbuntuNumberAnimation {} }173 height: optionsOverlayLoader.height
174 onOpenedChanged: optionsOverlayLoader.item.closeValueSelector()
175 enabled: camera.videoRecorder.recorderState == CameraRecorder.StoppedState
176 opacity: enabled ? 1.0 : 0.3
177
178 Item {
179 /* Use the 'trigger' feature of Panel so that tapping on the Panel
180 has the same effect as tapping outside of it (bottomEdgeClose) */
181 id: clickReceiver
182 anchors.fill: parent
183 function trigger() {
184 optionsOverlayClose();
185 }
186 }
187
188 property real progress: (bottomEdge.height - bottomEdge.position) / bottomEdge.height
189 property list<ListModel> options: [
190 ListModel {
191 id: gpsOptionsModel
192
193 property string settingsProperty: "gpsEnabled"
194 property string icon: "location"
195 property string label: ""
196 property bool isToggle: true
197 property int selectedIndex: bottomEdge.indexForValue(gpsOptionsModel, settings.gpsEnabled)
198 property bool available: true
199 property bool visible: true
200 property bool showInIndicators: true
201
202 ListElement {
203 icon: ""
204 label: QT_TR_NOOP("On")
205 value: true
206 }
207 ListElement {
208 icon: ""
209 label: QT_TR_NOOP("Off")
210 value: false
211 }
212 },
213 ListModel {
214 id: flashOptionsModel
215
216 property string settingsProperty: "flashMode"
217 property string icon: ""
218 property string label: ""
219 property bool isToggle: false
220 property int selectedIndex: bottomEdge.indexForValue(flashOptionsModel, settings.flashMode)
221 property bool available: camera.advanced.hasFlash
222 property bool visible: camera.captureMode == Camera.CaptureStillImage
223 property bool showInIndicators: true
224
225 ListElement {
226 icon: "flash-on"
227 label: QT_TR_NOOP("On")
228 value: Camera.FlashOn
229 }
230 ListElement {
231 icon: "flash-auto"
232 label: QT_TR_NOOP("Auto")
233 value: Camera.FlashAuto
234 }
235 ListElement {
236 icon: "flash-off"
237 label: QT_TR_NOOP("Off")
238 value: Camera.FlashOff
239 }
240 },
241 ListModel {
242 id: videoFlashOptionsModel
243
244 property string settingsProperty: "videoFlashMode"
245 property string icon: ""
246 property string label: ""
247 property bool isToggle: false
248 property int selectedIndex: bottomEdge.indexForValue(videoFlashOptionsModel, settings.videoFlashMode)
249 property bool available: camera.advanced.hasFlash
250 property bool visible: camera.captureMode == Camera.CaptureVideo
251 property bool showInIndicators: true
252
253 ListElement {
254 icon: "torch-on"
255 label: QT_TR_NOOP("On")
256 value: Camera.FlashVideoLight
257 }
258 ListElement {
259 icon: "torch-off"
260 label: QT_TR_NOOP("Off")
261 value: Camera.FlashOff
262 }
263 },
264 ListModel {
265 id: hdrOptionsModel
266
267 property string settingsProperty: "hdrEnabled"
268 property string icon: ""
269 property string label: i18n.tr("HDR")
270 property bool isToggle: true
271 property int selectedIndex: bottomEdge.indexForValue(hdrOptionsModel, settings.hdrEnabled)
272 property bool available: camera.advanced.hasHdr
273 property bool visible: true
274 property bool showInIndicators: true
275
276 ListElement {
277 icon: ""
278 label: QT_TR_NOOP("On")
279 value: true
280 }
281 ListElement {
282 icon: ""
283 label: QT_TR_NOOP("Off")
284 value: false
285 }
286 },
287 ListModel {
288 id: selfTimerOptionsModel
289
290 property string settingsProperty: "selfTimerDelay"
291 property string icon: ""
292 property string iconSource: "assets/self_timer.svg"
293 property string label: ""
294 property bool isToggle: true
295 property int selectedIndex: bottomEdge.indexForValue(selfTimerOptionsModel, settings.selfTimerDelay)
296 property bool available: true
297 property bool visible: true
298 property bool showInIndicators: true
299
300 ListElement {
301 icon: ""
302 label: QT_TR_NOOP("Off")
303 value: 0
304 }
305 ListElement {
306 icon: ""
307 label: QT_TR_NOOP("5 seconds")
308 value: 5
309 }
310 ListElement {
311 icon: ""
312 label: QT_TR_NOOP("15 seconds")
313 value: 15
314 }
315 },
316 ListModel {
317 id: encodingQualityOptionsModel
318
319 property string settingsProperty: "encodingQuality"
320 property string icon: "stock_image"
321 property string label: ""
322 property bool isToggle: false
323 property int selectedIndex: bottomEdge.indexForValue(encodingQualityOptionsModel, settings.encodingQuality)
324 property bool available: true
325 property bool visible: camera.captureMode == Camera.CaptureStillImage
326 property bool showInIndicators: false
327
328 ListElement {
329 label: QT_TR_NOOP("Fine Quality")
330 value: 4 // QMultimedia.VeryHighQuality
331 }
332 ListElement {
333 label: QT_TR_NOOP("Normal Quality")
334 value: 2 // QMultimedia.NormalQuality
335 }
336 ListElement {
337 label: QT_TR_NOOP("Basic Quality")
338 value: 1 // QMultimedia.LowQuality
339 }
340 },
341 ListModel {
342 id: gridOptionsModel
343
344 property string settingsProperty: "gridEnabled"
345 property string icon: ""
346 property string iconSource: "assets/grid_lines.svg"
347 property string label: ""
348 property bool isToggle: true
349 property int selectedIndex: bottomEdge.indexForValue(gridOptionsModel, settings.gridEnabled)
350 property bool available: true
351 property bool visible: true
352
353 ListElement {
354 icon: ""
355 label: QT_TR_NOOP("On")
356 value: true
357 }
358 ListElement {
359 icon: ""
360 label: QT_TR_NOOP("Off")
361 value: false
362 }
363 },
364 ListModel {
365 id: removableStorageOptionsModel
366
367 property string settingsProperty: "preferRemovableStorage"
368 property string icon: ""
369 property string label: i18n.tr("SD")
370 property bool isToggle: true
371 property int selectedIndex: bottomEdge.indexForValue(removableStorageOptionsModel, settings.preferRemovableStorage)
372 property bool available: application.removableStoragePresent
373 property bool visible: available
374
375 ListElement {
376 icon: ""
377 label: QT_TR_NOOP("Save to SD Card")
378 value: true
379 }
380 ListElement {
381 icon: ""
382 label: QT_TR_NOOP("Save internally")
383 value: false
384 }
385 },
386 ListModel {
387 id: videoResolutionOptionsModel
388
389 property string settingsProperty: "videoResolution"
390 property string icon: ""
391 property string label: "HD"
392 property bool isToggle: false
393 property int selectedIndex: bottomEdge.indexForValue(videoResolutionOptionsModel, settings.videoResolution)
394 property bool available: true
395 property bool visible: camera.captureMode == Camera.CaptureVideo
396 property bool showInIndicators: false
397 }
398 ]
399
400 /* FIXME: application.removableStoragePresent is not updated dynamically.
401 Workaround that by reading it when the bottom edge is opened/closed.
402 */
403 Connections {
404 target: bottomEdge
405 onOpenedChanged: removableStorageOptionsModel.available = application.removableStoragePresent
406 }
407
408 function indexForValue(model, value) {
409 var i;
410 var element;
411 for (i=0; i<model.count; i++) {
412 element = model.get(i);
413 if (element.value === value) {
414 return i;
415 }
416 }
417
418 return -1;
419 }
420
421 BottomEdgeIndicators {
422 id: bottomEdgeIndicators
423 options: bottomEdge.options
424 anchors {
425 horizontalCenter: parent.horizontalCenter
426 bottom: parent.top
427 }
428 opacity: bottomEdge.pressed || bottomEdge.opened ? 0.0 : 1.0
429 Behavior on opacity { UbuntuNumberAnimation {} }
430 }
431
432 Loader {
433 id: optionsOverlayLoader
434 anchors {
435 left: parent.left
436 right: parent.right
437 top: parent.top
438 }
439 asynchronous: true
440 sourceComponent: Component {
441 OptionsOverlay {
442 options: bottomEdge.options
443 }
444 }
445 }
425 }446 }
426 }447 }
427448
@@ -438,7 +459,7 @@
438 right: parent.right459 right: parent.right
439 }460 }
440 height: parent.height461 height: parent.height
441 y: bottomEdge.position - bottomEdge.height462 y: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation) == 0 ? bottomEdge.position - bottomEdge.height : 0
442 opacity: 1 - bottomEdge.progress463 opacity: 1 - bottomEdge.progress
443 visible: opacity != 0.0464 visible: opacity != 0.0
444 enabled: visible465 enabled: visible
@@ -656,9 +677,11 @@
656 anchors {677 anchors {
657 top: parent.top678 top: parent.top
658 bottom: shootButton.top679 bottom: shootButton.top
659 bottomMargin: units.gu(1)680 bottomMargin: bottomEdgeIndicators.height
660 left: parent.left681 left: parent.left
682 leftMargin: bottomEdgeIndicators.height
661 right: parent.right683 right: parent.right
684 rightMargin: bottomEdgeIndicators.height
662 }685 }
663686
664 property real initialZoom687 property real initialZoom
@@ -777,19 +800,4 @@
777 }800 }
778 }801 }
779 }802 }
780
781 Loader {
782 id: optionsOverlayLoader
783 anchors {
784 left: parent.left
785 right: parent.right
786 top: controls.bottom
787 }
788 asynchronous: true
789 sourceComponent: Component {
790 OptionsOverlay {
791 options: bottomEdge.options
792 }
793 }
794 }
795}803}
796804
=== modified file 'ViewFinderView.qml'
--- ViewFinderView.qml 2015-01-24 12:52:12 +0000
+++ ViewFinderView.qml 2015-07-03 18:21:53 +0000
@@ -296,7 +296,7 @@
296 onInViewChanged: if (!viewFinderView.inView) viewFinderOverlay.controls.cancelTimedShoot()296 onInViewChanged: if (!viewFinderView.inView) viewFinderOverlay.controls.cancelTimedShoot()
297 }297 }
298298
299 Item {299 OrientationHelper {
300 id: timedShootFeedback300 id: timedShootFeedback
301 anchors.fill: parent301 anchors.fill: parent
302302
303303
=== modified file 'camera-app.desktop.in.in'
--- camera-app.desktop.in.in 2014-09-03 18:52:03 +0000
+++ camera-app.desktop.in.in 2015-07-03 18:21:53 +0000
@@ -10,3 +10,4 @@
10X-Ubuntu-Touch=true10X-Ubuntu-Touch=true
11X-Ubuntu-Single-Instance=true11X-Ubuntu-Single-Instance=true
12X-Ubuntu-Default-Department-ID=accessories12X-Ubuntu-Default-Department-ID=accessories
13X-Ubuntu-Rotates-Window-Contents=true
1314
=== modified file 'camera-app.qml'
--- camera-app.qml 2015-04-24 12:12:23 +0000
+++ camera-app.qml 2015-07-03 18:21:53 +0000
@@ -69,10 +69,66 @@
69 Flickable {69 Flickable {
70 id: viewSwitcher70 id: viewSwitcher
71 anchors.fill: parent71 anchors.fill: parent
72 flickableDirection: Flickable.HorizontalFlick72 flickableDirection: state == "PORTRAIT" ? Flickable.HorizontalFlick : Flickable.VerticalFlick
73 boundsBehavior: Flickable.StopAtBounds73 boundsBehavior: Flickable.StopAtBounds
74 contentWidth: contentItem.childrenRect.width74
75 contentHeight: contentItem.childrenRect.height75 property real panesMargin: units.gu(1)
76 property real ratio
77 property int orientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
78 property var angleToOrientation: {0: "PORTRAIT",
79 90: "LANDSCAPE",
80 270: "INVERTED_LANDSCAPE"}
81 state: angleToOrientation[orientationAngle]
82 states: [
83 State {
84 name: "PORTRAIT"
85 StateChangeScript {
86 script: {
87 viewSwitcher.ratio = viewSwitcher.ratio;
88 viewSwitcher.contentWidth = Qt.binding(function() { return viewSwitcher.width * 2 + viewSwitcher.panesMargin });
89 viewSwitcher.contentHeight = Qt.binding(function() { return viewSwitcher.height });
90 galleryView.x = Qt.binding(function() { return viewFinderView.width + viewSwitcher.panesMargin });
91 galleryView.y = 0;
92 viewFinderView.x = 0;
93 viewFinderView.y = 0;
94 viewSwitcher.contentX = viewSwitcher.ratio * viewSwitcher.contentWidth;
95 viewSwitcher.ratio = Qt.binding(function() { return viewSwitcher.contentX / viewSwitcher.contentWidth });
96 }
97 }
98 },
99 State {
100 name: "LANDSCAPE"
101 StateChangeScript {
102 script: {
103 viewSwitcher.ratio = viewSwitcher.ratio;
104 viewSwitcher.contentWidth = Qt.binding(function() { return viewSwitcher.width });
105 viewSwitcher.contentHeight = Qt.binding(function() { return viewSwitcher.height * 2 + viewSwitcher.panesMargin });
106 galleryView.x = 0;
107 galleryView.y = Qt.binding(function() { return viewFinderView.height + viewSwitcher.panesMargin });
108 viewFinderView.x = 0;
109 viewFinderView.y = 0;
110 viewSwitcher.contentY = viewSwitcher.ratio * viewSwitcher.contentHeight;
111 viewSwitcher.ratio = Qt.binding(function() { return viewSwitcher.contentY / viewSwitcher.contentHeight });
112 }
113 }
114 },
115 State {
116 name: "INVERTED_LANDSCAPE"
117 StateChangeScript {
118 script: {
119 viewSwitcher.ratio = viewSwitcher.ratio;
120 viewSwitcher.contentWidth = Qt.binding(function() { return viewSwitcher.width });
121 viewSwitcher.contentHeight = Qt.binding(function() { return viewSwitcher.height * 2 + viewSwitcher.panesMargin });
122 galleryView.x = 0;
123 galleryView.y = 0;
124 viewFinderView.x = 0;
125 viewFinderView.y = Qt.binding(function() { return galleryView.height + viewSwitcher.panesMargin });
126 viewSwitcher.contentY = (0.5 - viewSwitcher.ratio) * viewSwitcher.contentHeight;
127 viewSwitcher.ratio = Qt.binding(function() { return 0.5 - viewSwitcher.contentY / viewSwitcher.contentHeight });
128 }
129 }
130 }
131 ]
76 interactive: !viewFinderView.touchAcquired && !galleryView.touchAcquired132 interactive: !viewFinderView.touchAcquired && !galleryView.touchAcquired
77133
78 Component.onCompleted: {134 Component.onCompleted: {
@@ -90,20 +146,35 @@
90 function settle() {146 function settle() {
91 settling = true;147 settling = true;
92 var velocity;148 var velocity;
93 if (horizontalVelocity < 0 || visibleArea.xPosition <= 0.05 || (horizontalVelocity == 0 && visibleArea.xPosition <= 0.25)) {149 if (flickableDirection == Flickable.HorizontalFlick) {
94 // FIXME: compute velocity better to ensure it reaches rest position (maybe in a constant time)150 if (horizontalVelocity < 0 || visibleArea.xPosition <= 0.05 || (horizontalVelocity == 0 && visibleArea.xPosition <= 0.25)) {
95 velocity = settleVelocity;151 // FIXME: compute velocity better to ensure it reaches rest position (maybe in a constant time)
152 velocity = settleVelocity;
153 } else {
154 velocity = -settleVelocity;
155 }
156 flick(velocity, 0);
96 } else {157 } else {
97 velocity = -settleVelocity;158 if (verticalVelocity < 0 || visibleArea.yPosition <= 0.05 || (verticalVelocity == 0 && visibleArea.yPosition <= 0.25)) {
159 // FIXME: compute velocity better to ensure it reaches rest position (maybe in a constant time)
160 velocity = settleVelocity;
161 } else {
162 velocity = -settleVelocity;
163 }
164 flick(0, velocity);
98 }165 }
99
100 flick(velocity, 0);
101 }166 }
102167
103 function switchToViewFinder() {168 function switchToViewFinder() {
104 cancelFlick();169 cancelFlick();
105 switching = true;170 switching = true;
106 flick(settleVelocity, 0);171 if (state == "PORTRAIT") {
172 flick(settleVelocity, 0);
173 } else if (state == "LANDSCAPE") {
174 flick(0, settleVelocity);
175 } else if (state == "INVERTED_LANDSCAPE") {
176 flick(0, -settleVelocity);
177 }
107 }178 }
108179
109 onMovementEnded: {180 onMovementEnded: {
@@ -139,39 +210,51 @@
139 }210 }
140 }211 }
141212
142 Row {213 onFlickingVerticallyChanged: {
143 id: viewsRow214 // use flickingHorizontallyChanged instead of flickEnded because flickEnded
144 anchors {215 // is not called when a flick is interrupted by the user
145 top: parent.top216 if (!flickingVertically) {
146 bottom: parent.bottom217 if (settling) {
147 }218 settling = false;
148 spacing: units.gu(1)219 }
149220 if (switching) {
150 ViewFinderView {221 switching = true;
151 id: viewFinderView222 }
152 width: viewSwitcher.width223 }
153 height: viewSwitcher.height224 }
154 overlayVisible: !viewSwitcher.moving && !viewSwitcher.flicking225
155 inView: !viewSwitcher.atXEnd226 onVerticalVelocityChanged: {
156 opacity: inView ? 1.0 : 0.0227 // FIXME: this is a workaround for the lack of notification when
157 onPhotoTaken: {228 // the user manually interrupts a flick by pressing and releasing
158 galleryView.prependMediaToModel(filePath);229 if (verticalVelocity == 0 && !atYBeginning && !atYEnd && !settling && !moving) {
159 galleryView.showLastPhotoTaken();230 settle();
160 }231 }
161 onVideoShot: {232 }
162 galleryView.prependMediaToModel(filePath);233
163 galleryView.showLastPhotoTaken();234 ViewFinderView {
164 }235 id: viewFinderView
165 }236 width: viewSwitcher.width
166237 height: viewSwitcher.height
167 GalleryViewLoader {238 overlayVisible: !viewSwitcher.moving && !viewSwitcher.flicking
168 id: galleryView239 inView: viewSwitcher.ratio < 0.5
169 width: viewSwitcher.width240 opacity: inView ? 1.0 : 0.0
170 height: viewSwitcher.height241 onPhotoTaken: {
171 inView: !viewSwitcher.atXBeginning242 galleryView.prependMediaToModel(filePath);
172 onExit: viewSwitcher.switchToViewFinder()243 galleryView.showLastPhotoTaken();
173 opacity: inView ? 1.0 : 0.0244 }
174 }245 onVideoShot: {
246 galleryView.prependMediaToModel(filePath);
247 galleryView.showLastPhotoTaken();
248 }
249 }
250
251 GalleryViewLoader {
252 id: galleryView
253 width: viewSwitcher.width
254 height: viewSwitcher.height
255 inView: viewSwitcher.ratio > 0.0
256 onExit: viewSwitcher.switchToViewFinder()
257 opacity: inView ? 1.0 : 0.0
175 }258 }
176 }259 }
177260
178261
=== modified file 'tests/autopilot/camera_app/tests/test_flash.py'
--- tests/autopilot/camera_app/tests/test_flash.py 2015-07-03 18:21:53 +0000
+++ tests/autopilot/camera_app/tests/test_flash.py 2015-07-03 18:21:53 +0000
@@ -112,12 +112,13 @@
112 self.assertThat(flash_button.iconName, Equals("flash-on"))112 self.assertThat(flash_button.iconName, Equals("flash-on"))
113113
114 # closes the flash options menu and open the hdr options menu114 # closes the flash options menu and open the hdr options menu
115 self.pointing_device.move_to_object(flash_button)
116 self.pointing_device.click()
117 self.assertThat(
118 option_value_selector.visible, Eventually(Equals(False)))
115 self.pointing_device.move_to_object(hdr_button)119 self.pointing_device.move_to_object(hdr_button)
116 self.pointing_device.click()120 self.pointing_device.click()
117 self.assertThat(121 self.assertThat(
118 option_value_selector.visible, Eventually(Equals(False)))
119 self.pointing_device.click()
120 self.assertThat(
121 option_value_selector.visible, Eventually(Equals(True)))122 option_value_selector.visible, Eventually(Equals(True)))
122123
123 # set hdr to "on" and verify that flash is "off"124 # set hdr to "on" and verify that flash is "off"
@@ -128,12 +129,13 @@
128 self.assertThat(hdr_button.on, Equals(True))129 self.assertThat(hdr_button.on, Equals(True))
129130
130 # closes the hdr options menu and open the flash options menu131 # closes the hdr options menu and open the flash options menu
132 self.pointing_device.move_to_object(hdr_button)
133 self.pointing_device.click()
134 self.assertThat(
135 option_value_selector.visible, Eventually(Equals(False)))
131 self.pointing_device.move_to_object(flash_button)136 self.pointing_device.move_to_object(flash_button)
132 self.pointing_device.click()137 self.pointing_device.click()
133 self.assertThat(138 self.assertThat(
134 option_value_selector.visible, Eventually(Equals(False)))
135 self.pointing_device.click()
136 self.assertThat(
137 option_value_selector.visible, Eventually(Equals(True)))139 option_value_selector.visible, Eventually(Equals(True)))
138140
139 # set flash to "on" and verify that hdr is "off"141 # set flash to "on" and verify that hdr is "off"

Subscribers

People subscribed via source and target branches