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
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"

Subscribers

People subscribed via source and target branches