Merge lp:~fboucault/camera-app/custom_rotation into lp:camera-app
- custom_rotation
- Merge into trunk
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 | ||||||||||||||||||||||||||||||||
Related bugs: |
|
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
Description of the change
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:570
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 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
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:572
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 573. By Florian Boucault
-
More appropriate bottom edge input area.
Bill Filler (bfiller) wrote : | # |
tested, works on arale and krillin
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:573
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Preview Diff
1 | === modified file 'CircleButton.qml' |
2 | --- CircleButton.qml 2014-12-05 18:57:32 +0000 |
3 | +++ CircleButton.qml 2015-07-03 18:21:53 +0000 |
4 | @@ -25,6 +25,7 @@ |
5 | property url iconSource |
6 | property bool on: true |
7 | property string label: "" |
8 | + property bool automaticOrientation: true |
9 | |
10 | width: units.gu(5) |
11 | height: width |
12 | @@ -49,7 +50,7 @@ |
13 | color: "white" |
14 | opacity: button.on ? (button.enabled ? 1.0 : 0.3): 0.5 |
15 | visible: label === "" |
16 | - rotation: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation) |
17 | + rotation: automaticOrientation ? Screen.angleBetween(Screen.primaryOrientation, Screen.orientation) : 0 |
18 | Behavior on rotation { |
19 | RotationAnimator { |
20 | duration: UbuntuAnimation.BriskDuration |
21 | @@ -69,7 +70,7 @@ |
22 | text: label |
23 | opacity: button.on ? (button.enabled ? 1.0 : 0.3): 0.5 |
24 | visible: label !== "" |
25 | - rotation: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation) |
26 | + rotation: automaticOrientation ? Screen.angleBetween(Screen.primaryOrientation, Screen.orientation) : 0 |
27 | Behavior on rotation { |
28 | RotationAnimator { |
29 | duration: UbuntuAnimation.BriskDuration |
30 | |
31 | === modified file 'OptionButton.qml' |
32 | --- OptionButton.qml 2015-01-27 11:32:45 +0000 |
33 | +++ OptionButton.qml 2015-07-03 18:21:53 +0000 |
34 | @@ -29,4 +29,5 @@ |
35 | enabled: model.available |
36 | label: model.label |
37 | visible: model.visible |
38 | + automaticOrientation: false |
39 | } |
40 | |
41 | === modified file 'OptionsOverlay.qml' |
42 | --- OptionsOverlay.qml 2015-01-27 15:22:59 +0000 |
43 | +++ OptionsOverlay.qml 2015-07-03 18:21:53 +0000 |
44 | @@ -37,7 +37,7 @@ |
45 | |
46 | columns: 3 |
47 | columnSpacing: units.gu(9.5) |
48 | - rowSpacing: units.gu(4) |
49 | + rowSpacing: units.gu(3) |
50 | |
51 | Repeater { |
52 | model: optionsOverlay.options |
53 | |
54 | === modified file 'PhotoRollHint.qml' |
55 | --- PhotoRollHint.qml 2014-11-05 10:17:23 +0000 |
56 | +++ PhotoRollHint.qml 2015-07-03 18:21:53 +0000 |
57 | @@ -40,33 +40,35 @@ |
58 | property alias photoRollHintNecessary: photoRollHint.necessary |
59 | } |
60 | |
61 | - Image { |
62 | - id: hintPictogram |
63 | - anchors { |
64 | - horizontalCenter: parent.horizontalCenter |
65 | - horizontalCenterOffset: units.gu(4) |
66 | - verticalCenter: parent.verticalCenter |
67 | - verticalCenterOffset: -units.gu(1) |
68 | - } |
69 | - |
70 | - asynchronous: true |
71 | - cache: false |
72 | - source: photoRollHint.enabled ? "assets/camera_swipe.png" : "" |
73 | - } |
74 | - |
75 | - Label { |
76 | - id: hintLabel |
77 | - |
78 | - anchors { |
79 | - top: hintPictogram.bottom |
80 | - topMargin: units.gu(5) |
81 | - horizontalCenter: parent.horizontalCenter |
82 | - } |
83 | - width: parent.width - 2 * units.gu(2) |
84 | - horizontalAlignment: Text.AlignHCenter |
85 | - wrapMode: Text.Wrap |
86 | - text: i18n.tr("Swipe left for photo roll") |
87 | - fontSize: "x-large" |
88 | - color: "#ebebeb" |
89 | + OrientationHelper { |
90 | + Image { |
91 | + id: hintPictogram |
92 | + anchors { |
93 | + horizontalCenter: parent.horizontalCenter |
94 | + horizontalCenterOffset: units.gu(4) |
95 | + verticalCenter: parent.verticalCenter |
96 | + verticalCenterOffset: -units.gu(1) |
97 | + } |
98 | + |
99 | + asynchronous: true |
100 | + cache: false |
101 | + source: photoRollHint.enabled ? "assets/camera_swipe.png" : "" |
102 | + } |
103 | + |
104 | + Label { |
105 | + id: hintLabel |
106 | + |
107 | + anchors { |
108 | + top: hintPictogram.bottom |
109 | + topMargin: units.gu(5) |
110 | + horizontalCenter: parent.horizontalCenter |
111 | + } |
112 | + width: parent.width - 2 * units.gu(2) |
113 | + horizontalAlignment: Text.AlignHCenter |
114 | + wrapMode: Text.Wrap |
115 | + text: i18n.tr("Swipe left for photo roll") |
116 | + fontSize: "x-large" |
117 | + color: "#ebebeb" |
118 | + } |
119 | } |
120 | } |
121 | |
122 | === modified file 'Snapshot.qml' |
123 | --- Snapshot.qml 2014-10-09 19:07:35 +0000 |
124 | +++ Snapshot.qml 2015-07-03 18:21:53 +0000 |
125 | @@ -15,6 +15,7 @@ |
126 | */ |
127 | |
128 | import QtQuick 2.2 |
129 | +import QtQuick.Window 2.0 |
130 | import Ubuntu.Components 1.0 |
131 | |
132 | Item { |
133 | @@ -34,11 +35,8 @@ |
134 | |
135 | Item { |
136 | id: container |
137 | - anchors { |
138 | - top: parent.top |
139 | - bottom: parent.bottom |
140 | - } |
141 | width: parent.width |
142 | + height: parent.height |
143 | |
144 | Image { |
145 | id: snapshot |
146 | @@ -69,15 +67,32 @@ |
147 | cache: false |
148 | } |
149 | } |
150 | + property int orientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation) |
151 | + property var angleToOrientation: {0: "PORTRAIT", |
152 | + 90: "LANDSCAPE", |
153 | + 270: "INVERTED_LANDSCAPE"} |
154 | |
155 | SequentialAnimation { |
156 | id: shoot |
157 | |
158 | PropertyAction { target: snapshotRoot; property: "visible"; value: true } |
159 | PauseAnimation { duration: 150 } |
160 | - XAnimator { target: container; to: container.width + shadow.width; duration: UbuntuAnimation.BriskDuration; easing: UbuntuAnimation.StandardEasing} |
161 | + XAnimator { |
162 | + target: container |
163 | + to: angleToOrientation[orientationAngle] == "PORTRAIT" ? container.width + shadow.width : 0 |
164 | + duration: UbuntuAnimation.BriskDuration |
165 | + easing: UbuntuAnimation.StandardEasing |
166 | + } |
167 | + YAnimator { |
168 | + target: container |
169 | + to: angleToOrientation[orientationAngle] == "LANDSCAPE" ? container.height + shadow.width : |
170 | + angleToOrientation[orientationAngle] == "INVERTED_LANDSCAPE" ? -(container.height + shadow.width) : 0 |
171 | + duration: UbuntuAnimation.BriskDuration |
172 | + easing: UbuntuAnimation.StandardEasing |
173 | + } |
174 | PropertyAction { target: snapshot; property: "source"; value: ""} |
175 | PropertyAction { target: snapshotRoot; property: "visible"; value: false } |
176 | PropertyAction { target: container; property: "x"; value: 0 } |
177 | + PropertyAction { target: container; property: "y"; value: 0 } |
178 | } |
179 | } |
180 | |
181 | === modified file 'ViewFinderOverlay.qml' |
182 | --- ViewFinderOverlay.qml 2015-03-13 00:41:54 +0000 |
183 | +++ ViewFinderOverlay.qml 2015-07-03 18:21:53 +0000 |
184 | @@ -159,269 +159,290 @@ |
185 | onClicked: optionsOverlayClose() |
186 | } |
187 | |
188 | - Panel { |
189 | - id: bottomEdge |
190 | - anchors { |
191 | - right: parent.right |
192 | - left: parent.left |
193 | - bottom: parent.bottom |
194 | - } |
195 | - height: optionsOverlayLoader.height |
196 | - onOpenedChanged: optionsOverlayLoader.item.closeValueSelector() |
197 | - enabled: camera.videoRecorder.recorderState == CameraRecorder.StoppedState |
198 | - opacity: enabled ? 1.0 : 0.3 |
199 | - |
200 | - Item { |
201 | - /* Use the 'trigger' feature of Panel so that tapping on the Panel |
202 | - has the same effect as tapping outside of it (bottomEdgeClose) */ |
203 | - id: clickReceiver |
204 | - anchors.fill: parent |
205 | - function trigger() { |
206 | - optionsOverlayClose(); |
207 | - } |
208 | - } |
209 | - |
210 | - property real progress: (bottomEdge.height - bottomEdge.position) / bottomEdge.height |
211 | - property list<ListModel> options: [ |
212 | - ListModel { |
213 | - id: gpsOptionsModel |
214 | - |
215 | - property string settingsProperty: "gpsEnabled" |
216 | - property string icon: "location" |
217 | - property string label: "" |
218 | - property bool isToggle: true |
219 | - property int selectedIndex: bottomEdge.indexForValue(gpsOptionsModel, settings.gpsEnabled) |
220 | - property bool available: true |
221 | - property bool visible: true |
222 | - property bool showInIndicators: true |
223 | - |
224 | - ListElement { |
225 | - icon: "" |
226 | - label: QT_TR_NOOP("On") |
227 | - value: true |
228 | - } |
229 | - ListElement { |
230 | - icon: "" |
231 | - label: QT_TR_NOOP("Off") |
232 | - value: false |
233 | - } |
234 | - }, |
235 | - ListModel { |
236 | - id: flashOptionsModel |
237 | - |
238 | - property string settingsProperty: "flashMode" |
239 | - property string icon: "" |
240 | - property string label: "" |
241 | - property bool isToggle: false |
242 | - property int selectedIndex: bottomEdge.indexForValue(flashOptionsModel, settings.flashMode) |
243 | - property bool available: camera.advanced.hasFlash |
244 | - property bool visible: camera.captureMode == Camera.CaptureStillImage |
245 | - property bool showInIndicators: true |
246 | - |
247 | - ListElement { |
248 | - icon: "flash-on" |
249 | - label: QT_TR_NOOP("On") |
250 | - value: Camera.FlashOn |
251 | - } |
252 | - ListElement { |
253 | - icon: "flash-auto" |
254 | - label: QT_TR_NOOP("Auto") |
255 | - value: Camera.FlashAuto |
256 | - } |
257 | - ListElement { |
258 | - icon: "flash-off" |
259 | - label: QT_TR_NOOP("Off") |
260 | - value: Camera.FlashOff |
261 | - } |
262 | - }, |
263 | - ListModel { |
264 | - id: videoFlashOptionsModel |
265 | - |
266 | - property string settingsProperty: "videoFlashMode" |
267 | - property string icon: "" |
268 | - property string label: "" |
269 | - property bool isToggle: false |
270 | - property int selectedIndex: bottomEdge.indexForValue(videoFlashOptionsModel, settings.videoFlashMode) |
271 | - property bool available: camera.advanced.hasFlash |
272 | - property bool visible: camera.captureMode == Camera.CaptureVideo |
273 | - property bool showInIndicators: true |
274 | - |
275 | - ListElement { |
276 | - icon: "torch-on" |
277 | - label: QT_TR_NOOP("On") |
278 | - value: Camera.FlashVideoLight |
279 | - } |
280 | - ListElement { |
281 | - icon: "torch-off" |
282 | - label: QT_TR_NOOP("Off") |
283 | - value: Camera.FlashOff |
284 | - } |
285 | - }, |
286 | - ListModel { |
287 | - id: hdrOptionsModel |
288 | - |
289 | - property string settingsProperty: "hdrEnabled" |
290 | - property string icon: "" |
291 | - property string label: i18n.tr("HDR") |
292 | - property bool isToggle: true |
293 | - property int selectedIndex: bottomEdge.indexForValue(hdrOptionsModel, settings.hdrEnabled) |
294 | - property bool available: camera.advanced.hasHdr |
295 | - property bool visible: true |
296 | - property bool showInIndicators: true |
297 | - |
298 | - ListElement { |
299 | - icon: "" |
300 | - label: QT_TR_NOOP("On") |
301 | - value: true |
302 | - } |
303 | - ListElement { |
304 | - icon: "" |
305 | - label: QT_TR_NOOP("Off") |
306 | - value: false |
307 | - } |
308 | - }, |
309 | - ListModel { |
310 | - id: selfTimerOptionsModel |
311 | - |
312 | - property string settingsProperty: "selfTimerDelay" |
313 | - property string icon: "" |
314 | - property string iconSource: "assets/self_timer.svg" |
315 | - property string label: "" |
316 | - property bool isToggle: true |
317 | - property int selectedIndex: bottomEdge.indexForValue(selfTimerOptionsModel, settings.selfTimerDelay) |
318 | - property bool available: true |
319 | - property bool visible: true |
320 | - property bool showInIndicators: true |
321 | - |
322 | - ListElement { |
323 | - icon: "" |
324 | - label: QT_TR_NOOP("Off") |
325 | - value: 0 |
326 | - } |
327 | - ListElement { |
328 | - icon: "" |
329 | - label: QT_TR_NOOP("5 seconds") |
330 | - value: 5 |
331 | - } |
332 | - ListElement { |
333 | - icon: "" |
334 | - label: QT_TR_NOOP("15 seconds") |
335 | - value: 15 |
336 | - } |
337 | - }, |
338 | - ListModel { |
339 | - id: encodingQualityOptionsModel |
340 | - |
341 | - property string settingsProperty: "encodingQuality" |
342 | - property string icon: "stock_image" |
343 | - property string label: "" |
344 | - property bool isToggle: false |
345 | - property int selectedIndex: bottomEdge.indexForValue(encodingQualityOptionsModel, settings.encodingQuality) |
346 | - property bool available: true |
347 | - property bool visible: camera.captureMode == Camera.CaptureStillImage |
348 | - property bool showInIndicators: false |
349 | - |
350 | - ListElement { |
351 | - label: QT_TR_NOOP("Fine Quality") |
352 | - value: 4 // QMultimedia.VeryHighQuality |
353 | - } |
354 | - ListElement { |
355 | - label: QT_TR_NOOP("Normal Quality") |
356 | - value: 2 // QMultimedia.NormalQuality |
357 | - } |
358 | - ListElement { |
359 | - label: QT_TR_NOOP("Basic Quality") |
360 | - value: 1 // QMultimedia.LowQuality |
361 | - } |
362 | - }, |
363 | - ListModel { |
364 | - id: gridOptionsModel |
365 | - |
366 | - property string settingsProperty: "gridEnabled" |
367 | - property string icon: "" |
368 | - property string iconSource: "assets/grid_lines.svg" |
369 | - property string label: "" |
370 | - property bool isToggle: true |
371 | - property int selectedIndex: bottomEdge.indexForValue(gridOptionsModel, settings.gridEnabled) |
372 | - property bool available: true |
373 | - property bool visible: true |
374 | - |
375 | - ListElement { |
376 | - icon: "" |
377 | - label: QT_TR_NOOP("On") |
378 | - value: true |
379 | - } |
380 | - ListElement { |
381 | - icon: "" |
382 | - label: QT_TR_NOOP("Off") |
383 | - value: false |
384 | - } |
385 | - }, |
386 | - ListModel { |
387 | - id: removableStorageOptionsModel |
388 | - |
389 | - property string settingsProperty: "preferRemovableStorage" |
390 | - property string icon: "" |
391 | - property string label: i18n.tr("SD") |
392 | - property bool isToggle: true |
393 | - property int selectedIndex: bottomEdge.indexForValue(removableStorageOptionsModel, settings.preferRemovableStorage) |
394 | - property bool available: application.removableStoragePresent |
395 | - property bool visible: available |
396 | - |
397 | - ListElement { |
398 | - icon: "" |
399 | - label: QT_TR_NOOP("Save to SD Card") |
400 | - value: true |
401 | - } |
402 | - ListElement { |
403 | - icon: "" |
404 | - label: QT_TR_NOOP("Save internally") |
405 | - value: false |
406 | - } |
407 | - }, |
408 | - ListModel { |
409 | - id: videoResolutionOptionsModel |
410 | - |
411 | - property string settingsProperty: "videoResolution" |
412 | - property string icon: "" |
413 | - property string label: "HD" |
414 | - property bool isToggle: false |
415 | - property int selectedIndex: bottomEdge.indexForValue(videoResolutionOptionsModel, settings.videoResolution) |
416 | - property bool available: true |
417 | - property bool visible: camera.captureMode == Camera.CaptureVideo |
418 | - property bool showInIndicators: false |
419 | - } |
420 | - ] |
421 | - |
422 | - /* FIXME: application.removableStoragePresent is not updated dynamically. |
423 | - Workaround that by reading it when the bottom edge is opened/closed. |
424 | - */ |
425 | - Connections { |
426 | - target: bottomEdge |
427 | - onOpenedChanged: removableStorageOptionsModel.available = application.removableStoragePresent |
428 | - } |
429 | - |
430 | - function indexForValue(model, value) { |
431 | - var i; |
432 | - var element; |
433 | - for (i=0; i<model.count; i++) { |
434 | - element = model.get(i); |
435 | - if (element.value === value) { |
436 | - return i; |
437 | - } |
438 | - } |
439 | - |
440 | - return -1; |
441 | - } |
442 | - |
443 | - BottomEdgeIndicators { |
444 | - options: bottomEdge.options |
445 | + OrientationHelper { |
446 | + id: bottomEdgeOrientation |
447 | + transitionEnabled: bottomEdge.opened |
448 | + |
449 | + Panel { |
450 | + id: bottomEdge |
451 | anchors { |
452 | - horizontalCenter: parent.horizontalCenter |
453 | - bottom: parent.top |
454 | - } |
455 | - opacity: bottomEdge.pressed || bottomEdge.opened ? 0.0 : 1.0 |
456 | - Behavior on opacity { UbuntuNumberAnimation {} } |
457 | + right: parent.right |
458 | + left: parent.left |
459 | + bottom: parent.bottom |
460 | + } |
461 | + height: optionsOverlayLoader.height |
462 | + onOpenedChanged: optionsOverlayLoader.item.closeValueSelector() |
463 | + enabled: camera.videoRecorder.recorderState == CameraRecorder.StoppedState |
464 | + opacity: enabled ? 1.0 : 0.3 |
465 | + |
466 | + Item { |
467 | + /* Use the 'trigger' feature of Panel so that tapping on the Panel |
468 | + has the same effect as tapping outside of it (bottomEdgeClose) */ |
469 | + id: clickReceiver |
470 | + anchors.fill: parent |
471 | + function trigger() { |
472 | + optionsOverlayClose(); |
473 | + } |
474 | + } |
475 | + |
476 | + property real progress: (bottomEdge.height - bottomEdge.position) / bottomEdge.height |
477 | + property list<ListModel> options: [ |
478 | + ListModel { |
479 | + id: gpsOptionsModel |
480 | + |
481 | + property string settingsProperty: "gpsEnabled" |
482 | + property string icon: "location" |
483 | + property string label: "" |
484 | + property bool isToggle: true |
485 | + property int selectedIndex: bottomEdge.indexForValue(gpsOptionsModel, settings.gpsEnabled) |
486 | + property bool available: true |
487 | + property bool visible: true |
488 | + property bool showInIndicators: true |
489 | + |
490 | + ListElement { |
491 | + icon: "" |
492 | + label: QT_TR_NOOP("On") |
493 | + value: true |
494 | + } |
495 | + ListElement { |
496 | + icon: "" |
497 | + label: QT_TR_NOOP("Off") |
498 | + value: false |
499 | + } |
500 | + }, |
501 | + ListModel { |
502 | + id: flashOptionsModel |
503 | + |
504 | + property string settingsProperty: "flashMode" |
505 | + property string icon: "" |
506 | + property string label: "" |
507 | + property bool isToggle: false |
508 | + property int selectedIndex: bottomEdge.indexForValue(flashOptionsModel, settings.flashMode) |
509 | + property bool available: camera.advanced.hasFlash |
510 | + property bool visible: camera.captureMode == Camera.CaptureStillImage |
511 | + property bool showInIndicators: true |
512 | + |
513 | + ListElement { |
514 | + icon: "flash-on" |
515 | + label: QT_TR_NOOP("On") |
516 | + value: Camera.FlashOn |
517 | + } |
518 | + ListElement { |
519 | + icon: "flash-auto" |
520 | + label: QT_TR_NOOP("Auto") |
521 | + value: Camera.FlashAuto |
522 | + } |
523 | + ListElement { |
524 | + icon: "flash-off" |
525 | + label: QT_TR_NOOP("Off") |
526 | + value: Camera.FlashOff |
527 | + } |
528 | + }, |
529 | + ListModel { |
530 | + id: videoFlashOptionsModel |
531 | + |
532 | + property string settingsProperty: "videoFlashMode" |
533 | + property string icon: "" |
534 | + property string label: "" |
535 | + property bool isToggle: false |
536 | + property int selectedIndex: bottomEdge.indexForValue(videoFlashOptionsModel, settings.videoFlashMode) |
537 | + property bool available: camera.advanced.hasFlash |
538 | + property bool visible: camera.captureMode == Camera.CaptureVideo |
539 | + property bool showInIndicators: true |
540 | + |
541 | + ListElement { |
542 | + icon: "torch-on" |
543 | + label: QT_TR_NOOP("On") |
544 | + value: Camera.FlashVideoLight |
545 | + } |
546 | + ListElement { |
547 | + icon: "torch-off" |
548 | + label: QT_TR_NOOP("Off") |
549 | + value: Camera.FlashOff |
550 | + } |
551 | + }, |
552 | + ListModel { |
553 | + id: hdrOptionsModel |
554 | + |
555 | + property string settingsProperty: "hdrEnabled" |
556 | + property string icon: "" |
557 | + property string label: i18n.tr("HDR") |
558 | + property bool isToggle: true |
559 | + property int selectedIndex: bottomEdge.indexForValue(hdrOptionsModel, settings.hdrEnabled) |
560 | + property bool available: camera.advanced.hasHdr |
561 | + property bool visible: true |
562 | + property bool showInIndicators: true |
563 | + |
564 | + ListElement { |
565 | + icon: "" |
566 | + label: QT_TR_NOOP("On") |
567 | + value: true |
568 | + } |
569 | + ListElement { |
570 | + icon: "" |
571 | + label: QT_TR_NOOP("Off") |
572 | + value: false |
573 | + } |
574 | + }, |
575 | + ListModel { |
576 | + id: selfTimerOptionsModel |
577 | + |
578 | + property string settingsProperty: "selfTimerDelay" |
579 | + property string icon: "" |
580 | + property string iconSource: "assets/self_timer.svg" |
581 | + property string label: "" |
582 | + property bool isToggle: true |
583 | + property int selectedIndex: bottomEdge.indexForValue(selfTimerOptionsModel, settings.selfTimerDelay) |
584 | + property bool available: true |
585 | + property bool visible: true |
586 | + property bool showInIndicators: true |
587 | + |
588 | + ListElement { |
589 | + icon: "" |
590 | + label: QT_TR_NOOP("Off") |
591 | + value: 0 |
592 | + } |
593 | + ListElement { |
594 | + icon: "" |
595 | + label: QT_TR_NOOP("5 seconds") |
596 | + value: 5 |
597 | + } |
598 | + ListElement { |
599 | + icon: "" |
600 | + label: QT_TR_NOOP("15 seconds") |
601 | + value: 15 |
602 | + } |
603 | + }, |
604 | + ListModel { |
605 | + id: encodingQualityOptionsModel |
606 | + |
607 | + property string settingsProperty: "encodingQuality" |
608 | + property string icon: "stock_image" |
609 | + property string label: "" |
610 | + property bool isToggle: false |
611 | + property int selectedIndex: bottomEdge.indexForValue(encodingQualityOptionsModel, settings.encodingQuality) |
612 | + property bool available: true |
613 | + property bool visible: camera.captureMode == Camera.CaptureStillImage |
614 | + property bool showInIndicators: false |
615 | + |
616 | + ListElement { |
617 | + label: QT_TR_NOOP("Fine Quality") |
618 | + value: 4 // QMultimedia.VeryHighQuality |
619 | + } |
620 | + ListElement { |
621 | + label: QT_TR_NOOP("Normal Quality") |
622 | + value: 2 // QMultimedia.NormalQuality |
623 | + } |
624 | + ListElement { |
625 | + label: QT_TR_NOOP("Basic Quality") |
626 | + value: 1 // QMultimedia.LowQuality |
627 | + } |
628 | + }, |
629 | + ListModel { |
630 | + id: gridOptionsModel |
631 | + |
632 | + property string settingsProperty: "gridEnabled" |
633 | + property string icon: "" |
634 | + property string iconSource: "assets/grid_lines.svg" |
635 | + property string label: "" |
636 | + property bool isToggle: true |
637 | + property int selectedIndex: bottomEdge.indexForValue(gridOptionsModel, settings.gridEnabled) |
638 | + property bool available: true |
639 | + property bool visible: true |
640 | + |
641 | + ListElement { |
642 | + icon: "" |
643 | + label: QT_TR_NOOP("On") |
644 | + value: true |
645 | + } |
646 | + ListElement { |
647 | + icon: "" |
648 | + label: QT_TR_NOOP("Off") |
649 | + value: false |
650 | + } |
651 | + }, |
652 | + ListModel { |
653 | + id: removableStorageOptionsModel |
654 | + |
655 | + property string settingsProperty: "preferRemovableStorage" |
656 | + property string icon: "" |
657 | + property string label: i18n.tr("SD") |
658 | + property bool isToggle: true |
659 | + property int selectedIndex: bottomEdge.indexForValue(removableStorageOptionsModel, settings.preferRemovableStorage) |
660 | + property bool available: application.removableStoragePresent |
661 | + property bool visible: available |
662 | + |
663 | + ListElement { |
664 | + icon: "" |
665 | + label: QT_TR_NOOP("Save to SD Card") |
666 | + value: true |
667 | + } |
668 | + ListElement { |
669 | + icon: "" |
670 | + label: QT_TR_NOOP("Save internally") |
671 | + value: false |
672 | + } |
673 | + }, |
674 | + ListModel { |
675 | + id: videoResolutionOptionsModel |
676 | + |
677 | + property string settingsProperty: "videoResolution" |
678 | + property string icon: "" |
679 | + property string label: "HD" |
680 | + property bool isToggle: false |
681 | + property int selectedIndex: bottomEdge.indexForValue(videoResolutionOptionsModel, settings.videoResolution) |
682 | + property bool available: true |
683 | + property bool visible: camera.captureMode == Camera.CaptureVideo |
684 | + property bool showInIndicators: false |
685 | + } |
686 | + ] |
687 | + |
688 | + /* FIXME: application.removableStoragePresent is not updated dynamically. |
689 | + Workaround that by reading it when the bottom edge is opened/closed. |
690 | + */ |
691 | + Connections { |
692 | + target: bottomEdge |
693 | + onOpenedChanged: removableStorageOptionsModel.available = application.removableStoragePresent |
694 | + } |
695 | + |
696 | + function indexForValue(model, value) { |
697 | + var i; |
698 | + var element; |
699 | + for (i=0; i<model.count; i++) { |
700 | + element = model.get(i); |
701 | + if (element.value === value) { |
702 | + return i; |
703 | + } |
704 | + } |
705 | + |
706 | + return -1; |
707 | + } |
708 | + |
709 | + BottomEdgeIndicators { |
710 | + id: bottomEdgeIndicators |
711 | + options: bottomEdge.options |
712 | + anchors { |
713 | + horizontalCenter: parent.horizontalCenter |
714 | + bottom: parent.top |
715 | + } |
716 | + opacity: bottomEdge.pressed || bottomEdge.opened ? 0.0 : 1.0 |
717 | + Behavior on opacity { UbuntuNumberAnimation {} } |
718 | + } |
719 | + |
720 | + Loader { |
721 | + id: optionsOverlayLoader |
722 | + anchors { |
723 | + left: parent.left |
724 | + right: parent.right |
725 | + top: parent.top |
726 | + } |
727 | + asynchronous: true |
728 | + sourceComponent: Component { |
729 | + OptionsOverlay { |
730 | + options: bottomEdge.options |
731 | + } |
732 | + } |
733 | + } |
734 | } |
735 | } |
736 | |
737 | @@ -438,7 +459,7 @@ |
738 | right: parent.right |
739 | } |
740 | height: parent.height |
741 | - y: bottomEdge.position - bottomEdge.height |
742 | + y: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation) == 0 ? bottomEdge.position - bottomEdge.height : 0 |
743 | opacity: 1 - bottomEdge.progress |
744 | visible: opacity != 0.0 |
745 | enabled: visible |
746 | @@ -656,9 +677,11 @@ |
747 | anchors { |
748 | top: parent.top |
749 | bottom: shootButton.top |
750 | - bottomMargin: units.gu(1) |
751 | + bottomMargin: bottomEdgeIndicators.height |
752 | left: parent.left |
753 | + leftMargin: bottomEdgeIndicators.height |
754 | right: parent.right |
755 | + rightMargin: bottomEdgeIndicators.height |
756 | } |
757 | |
758 | property real initialZoom |
759 | @@ -777,19 +800,4 @@ |
760 | } |
761 | } |
762 | } |
763 | - |
764 | - Loader { |
765 | - id: optionsOverlayLoader |
766 | - anchors { |
767 | - left: parent.left |
768 | - right: parent.right |
769 | - top: controls.bottom |
770 | - } |
771 | - asynchronous: true |
772 | - sourceComponent: Component { |
773 | - OptionsOverlay { |
774 | - options: bottomEdge.options |
775 | - } |
776 | - } |
777 | - } |
778 | } |
779 | |
780 | === modified file 'ViewFinderView.qml' |
781 | --- ViewFinderView.qml 2015-01-24 12:52:12 +0000 |
782 | +++ ViewFinderView.qml 2015-07-03 18:21:53 +0000 |
783 | @@ -296,7 +296,7 @@ |
784 | onInViewChanged: if (!viewFinderView.inView) viewFinderOverlay.controls.cancelTimedShoot() |
785 | } |
786 | |
787 | - Item { |
788 | + OrientationHelper { |
789 | id: timedShootFeedback |
790 | anchors.fill: parent |
791 | |
792 | |
793 | === modified file 'camera-app.desktop.in.in' |
794 | --- camera-app.desktop.in.in 2014-09-03 18:52:03 +0000 |
795 | +++ camera-app.desktop.in.in 2015-07-03 18:21:53 +0000 |
796 | @@ -10,3 +10,4 @@ |
797 | X-Ubuntu-Touch=true |
798 | X-Ubuntu-Single-Instance=true |
799 | X-Ubuntu-Default-Department-ID=accessories |
800 | +X-Ubuntu-Rotates-Window-Contents=true |
801 | |
802 | === modified file 'camera-app.qml' |
803 | --- camera-app.qml 2015-04-24 12:12:23 +0000 |
804 | +++ camera-app.qml 2015-07-03 18:21:53 +0000 |
805 | @@ -69,10 +69,66 @@ |
806 | Flickable { |
807 | id: viewSwitcher |
808 | anchors.fill: parent |
809 | - flickableDirection: Flickable.HorizontalFlick |
810 | + flickableDirection: state == "PORTRAIT" ? Flickable.HorizontalFlick : Flickable.VerticalFlick |
811 | boundsBehavior: Flickable.StopAtBounds |
812 | - contentWidth: contentItem.childrenRect.width |
813 | - contentHeight: contentItem.childrenRect.height |
814 | + |
815 | + property real panesMargin: units.gu(1) |
816 | + property real ratio |
817 | + property int orientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation) |
818 | + property var angleToOrientation: {0: "PORTRAIT", |
819 | + 90: "LANDSCAPE", |
820 | + 270: "INVERTED_LANDSCAPE"} |
821 | + state: angleToOrientation[orientationAngle] |
822 | + states: [ |
823 | + State { |
824 | + name: "PORTRAIT" |
825 | + StateChangeScript { |
826 | + script: { |
827 | + viewSwitcher.ratio = viewSwitcher.ratio; |
828 | + viewSwitcher.contentWidth = Qt.binding(function() { return viewSwitcher.width * 2 + viewSwitcher.panesMargin }); |
829 | + viewSwitcher.contentHeight = Qt.binding(function() { return viewSwitcher.height }); |
830 | + galleryView.x = Qt.binding(function() { return viewFinderView.width + viewSwitcher.panesMargin }); |
831 | + galleryView.y = 0; |
832 | + viewFinderView.x = 0; |
833 | + viewFinderView.y = 0; |
834 | + viewSwitcher.contentX = viewSwitcher.ratio * viewSwitcher.contentWidth; |
835 | + viewSwitcher.ratio = Qt.binding(function() { return viewSwitcher.contentX / viewSwitcher.contentWidth }); |
836 | + } |
837 | + } |
838 | + }, |
839 | + State { |
840 | + name: "LANDSCAPE" |
841 | + StateChangeScript { |
842 | + script: { |
843 | + viewSwitcher.ratio = viewSwitcher.ratio; |
844 | + viewSwitcher.contentWidth = Qt.binding(function() { return viewSwitcher.width }); |
845 | + viewSwitcher.contentHeight = Qt.binding(function() { return viewSwitcher.height * 2 + viewSwitcher.panesMargin }); |
846 | + galleryView.x = 0; |
847 | + galleryView.y = Qt.binding(function() { return viewFinderView.height + viewSwitcher.panesMargin }); |
848 | + viewFinderView.x = 0; |
849 | + viewFinderView.y = 0; |
850 | + viewSwitcher.contentY = viewSwitcher.ratio * viewSwitcher.contentHeight; |
851 | + viewSwitcher.ratio = Qt.binding(function() { return viewSwitcher.contentY / viewSwitcher.contentHeight }); |
852 | + } |
853 | + } |
854 | + }, |
855 | + State { |
856 | + name: "INVERTED_LANDSCAPE" |
857 | + StateChangeScript { |
858 | + script: { |
859 | + viewSwitcher.ratio = viewSwitcher.ratio; |
860 | + viewSwitcher.contentWidth = Qt.binding(function() { return viewSwitcher.width }); |
861 | + viewSwitcher.contentHeight = Qt.binding(function() { return viewSwitcher.height * 2 + viewSwitcher.panesMargin }); |
862 | + galleryView.x = 0; |
863 | + galleryView.y = 0; |
864 | + viewFinderView.x = 0; |
865 | + viewFinderView.y = Qt.binding(function() { return galleryView.height + viewSwitcher.panesMargin }); |
866 | + viewSwitcher.contentY = (0.5 - viewSwitcher.ratio) * viewSwitcher.contentHeight; |
867 | + viewSwitcher.ratio = Qt.binding(function() { return 0.5 - viewSwitcher.contentY / viewSwitcher.contentHeight }); |
868 | + } |
869 | + } |
870 | + } |
871 | + ] |
872 | interactive: !viewFinderView.touchAcquired && !galleryView.touchAcquired |
873 | |
874 | Component.onCompleted: { |
875 | @@ -90,20 +146,35 @@ |
876 | function settle() { |
877 | settling = true; |
878 | var velocity; |
879 | - if (horizontalVelocity < 0 || visibleArea.xPosition <= 0.05 || (horizontalVelocity == 0 && visibleArea.xPosition <= 0.25)) { |
880 | - // FIXME: compute velocity better to ensure it reaches rest position (maybe in a constant time) |
881 | - velocity = settleVelocity; |
882 | + if (flickableDirection == Flickable.HorizontalFlick) { |
883 | + if (horizontalVelocity < 0 || visibleArea.xPosition <= 0.05 || (horizontalVelocity == 0 && visibleArea.xPosition <= 0.25)) { |
884 | + // FIXME: compute velocity better to ensure it reaches rest position (maybe in a constant time) |
885 | + velocity = settleVelocity; |
886 | + } else { |
887 | + velocity = -settleVelocity; |
888 | + } |
889 | + flick(velocity, 0); |
890 | } else { |
891 | - velocity = -settleVelocity; |
892 | + if (verticalVelocity < 0 || visibleArea.yPosition <= 0.05 || (verticalVelocity == 0 && visibleArea.yPosition <= 0.25)) { |
893 | + // FIXME: compute velocity better to ensure it reaches rest position (maybe in a constant time) |
894 | + velocity = settleVelocity; |
895 | + } else { |
896 | + velocity = -settleVelocity; |
897 | + } |
898 | + flick(0, velocity); |
899 | } |
900 | - |
901 | - flick(velocity, 0); |
902 | } |
903 | |
904 | function switchToViewFinder() { |
905 | cancelFlick(); |
906 | switching = true; |
907 | - flick(settleVelocity, 0); |
908 | + if (state == "PORTRAIT") { |
909 | + flick(settleVelocity, 0); |
910 | + } else if (state == "LANDSCAPE") { |
911 | + flick(0, settleVelocity); |
912 | + } else if (state == "INVERTED_LANDSCAPE") { |
913 | + flick(0, -settleVelocity); |
914 | + } |
915 | } |
916 | |
917 | onMovementEnded: { |
918 | @@ -139,39 +210,51 @@ |
919 | } |
920 | } |
921 | |
922 | - Row { |
923 | - id: viewsRow |
924 | - anchors { |
925 | - top: parent.top |
926 | - bottom: parent.bottom |
927 | - } |
928 | - spacing: units.gu(1) |
929 | - |
930 | - ViewFinderView { |
931 | - id: viewFinderView |
932 | - width: viewSwitcher.width |
933 | - height: viewSwitcher.height |
934 | - overlayVisible: !viewSwitcher.moving && !viewSwitcher.flicking |
935 | - inView: !viewSwitcher.atXEnd |
936 | - opacity: inView ? 1.0 : 0.0 |
937 | - onPhotoTaken: { |
938 | - galleryView.prependMediaToModel(filePath); |
939 | - galleryView.showLastPhotoTaken(); |
940 | - } |
941 | - onVideoShot: { |
942 | - galleryView.prependMediaToModel(filePath); |
943 | - galleryView.showLastPhotoTaken(); |
944 | - } |
945 | - } |
946 | - |
947 | - GalleryViewLoader { |
948 | - id: galleryView |
949 | - width: viewSwitcher.width |
950 | - height: viewSwitcher.height |
951 | - inView: !viewSwitcher.atXBeginning |
952 | - onExit: viewSwitcher.switchToViewFinder() |
953 | - opacity: inView ? 1.0 : 0.0 |
954 | - } |
955 | + onFlickingVerticallyChanged: { |
956 | + // use flickingHorizontallyChanged instead of flickEnded because flickEnded |
957 | + // is not called when a flick is interrupted by the user |
958 | + if (!flickingVertically) { |
959 | + if (settling) { |
960 | + settling = false; |
961 | + } |
962 | + if (switching) { |
963 | + switching = true; |
964 | + } |
965 | + } |
966 | + } |
967 | + |
968 | + onVerticalVelocityChanged: { |
969 | + // FIXME: this is a workaround for the lack of notification when |
970 | + // the user manually interrupts a flick by pressing and releasing |
971 | + if (verticalVelocity == 0 && !atYBeginning && !atYEnd && !settling && !moving) { |
972 | + settle(); |
973 | + } |
974 | + } |
975 | + |
976 | + ViewFinderView { |
977 | + id: viewFinderView |
978 | + width: viewSwitcher.width |
979 | + height: viewSwitcher.height |
980 | + overlayVisible: !viewSwitcher.moving && !viewSwitcher.flicking |
981 | + inView: viewSwitcher.ratio < 0.5 |
982 | + opacity: inView ? 1.0 : 0.0 |
983 | + onPhotoTaken: { |
984 | + galleryView.prependMediaToModel(filePath); |
985 | + galleryView.showLastPhotoTaken(); |
986 | + } |
987 | + onVideoShot: { |
988 | + galleryView.prependMediaToModel(filePath); |
989 | + galleryView.showLastPhotoTaken(); |
990 | + } |
991 | + } |
992 | + |
993 | + GalleryViewLoader { |
994 | + id: galleryView |
995 | + width: viewSwitcher.width |
996 | + height: viewSwitcher.height |
997 | + inView: viewSwitcher.ratio > 0.0 |
998 | + onExit: viewSwitcher.switchToViewFinder() |
999 | + opacity: inView ? 1.0 : 0.0 |
1000 | } |
1001 | } |
1002 | |
1003 | |
1004 | === modified file 'tests/autopilot/camera_app/tests/test_flash.py' |
1005 | --- tests/autopilot/camera_app/tests/test_flash.py 2015-07-03 18:21:53 +0000 |
1006 | +++ tests/autopilot/camera_app/tests/test_flash.py 2015-07-03 18:21:53 +0000 |
1007 | @@ -112,12 +112,13 @@ |
1008 | self.assertThat(flash_button.iconName, Equals("flash-on")) |
1009 | |
1010 | # closes the flash options menu and open the hdr options menu |
1011 | + self.pointing_device.move_to_object(flash_button) |
1012 | + self.pointing_device.click() |
1013 | + self.assertThat( |
1014 | + option_value_selector.visible, Eventually(Equals(False))) |
1015 | self.pointing_device.move_to_object(hdr_button) |
1016 | self.pointing_device.click() |
1017 | self.assertThat( |
1018 | - option_value_selector.visible, Eventually(Equals(False))) |
1019 | - self.pointing_device.click() |
1020 | - self.assertThat( |
1021 | option_value_selector.visible, Eventually(Equals(True))) |
1022 | |
1023 | # set hdr to "on" and verify that flash is "off" |
1024 | @@ -128,12 +129,13 @@ |
1025 | self.assertThat(hdr_button.on, Equals(True)) |
1026 | |
1027 | # closes the hdr options menu and open the flash options menu |
1028 | + self.pointing_device.move_to_object(hdr_button) |
1029 | + self.pointing_device.click() |
1030 | + self.assertThat( |
1031 | + option_value_selector.visible, Eventually(Equals(False))) |
1032 | self.pointing_device.move_to_object(flash_button) |
1033 | self.pointing_device.click() |
1034 | self.assertThat( |
1035 | - option_value_selector.visible, Eventually(Equals(False))) |
1036 | - self.pointing_device.click() |
1037 | - self.assertThat( |
1038 | option_value_selector.visible, Eventually(Equals(True))) |
1039 | |
1040 | # set flash to "on" and verify that hdr is "off" |
FAILED: Continuous integration, rev:567 jenkins. qa.ubuntu. com/job/ camera- app-ci/ 428/ jenkins. qa.ubuntu. com/job/ camera- app-vivid- amd64-ci/ 124 jenkins. qa.ubuntu. com/job/ camera- app-vivid- armhf-ci/ 124 jenkins. qa.ubuntu. com/job/ camera- app-vivid- armhf-ci/ 124/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ camera- app-vivid- i386-ci/ 124 jenkins. qa.ubuntu. com/job/ generic- click-autopilot -vivid- touch/236 jenkins. qa.ubuntu. com/job/ generic- click-autopilot -runner- mako/888 jenkins. qa.ubuntu. com/job/ generic- click-builder- vivid-armhf/ 639 s-jenkins. ubuntu- ci:8080/ job/touch- flash-device/ 21555
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/camera- app-ci/ 428/rebuild
http://