Merge lp:camera-app/staging into lp:camera-app

Proposed by Florian Boucault
Status: Merged
Approved by: Florian Boucault
Approved revision: 686
Merged at revision: 643
Proposed branch: lp:camera-app/staging
Merge into: lp:camera-app
Diff against target: 1655 lines (+587/-221)
25 files modified
GalleryView.qml (+4/-4)
GalleryViewHeader.qml (+3/-0)
MimeTypeMapper.js (+1/-1)
OptionButton.qml (+2/-2)
PhotogridView.qml (+42/-6)
SlideshowView.qml (+16/-6)
UnableShareDialog.qml (+34/-0)
ViewFinderOverlay.qml (+16/-9)
ViewFinderOverlayLoader.qml (+3/-1)
ViewFinderView.qml (+14/-15)
camera-app.qml (+41/-18)
debian/control (+3/-0)
tests/autopilot/camera_app/emulators/main_window.py (+77/-9)
tests/autopilot/camera_app/emulators/panel.py (+8/-2)
tests/autopilot/camera_app/tests/__init__.py (+14/-8)
tests/autopilot/camera_app/tests/test_capture.py (+67/-20)
tests/autopilot/camera_app/tests/test_diskspace.py (+4/-7)
tests/autopilot/camera_app/tests/test_flash.py (+12/-13)
tests/autopilot/camera_app/tests/test_focus.py (+17/-18)
tests/autopilot/camera_app/tests/test_gallery_view.py (+118/-41)
tests/autopilot/camera_app/tests/test_options.py (+0/-8)
tests/autopilot/camera_app/tests/test_photo_editor.py (+2/-22)
tests/autopilot/camera_app/tests/test_zoom.py (+0/-10)
tests/unittests/CMakeLists.txt (+1/-1)
tests/unittests/tst_PhotogridView.qml (+88/-0)
To merge this branch: bzr merge lp:camera-app/staging
Reviewer Review Type Date Requested Status
Ubuntu Phablet Team Pending
Review via email: mp+288646@code.launchpad.net

Commit message

New release:
- Only accept manual focus on points inside the viewfinder.
- viewFinderView.captureMode can now be set before the camera is ready. Take advantage of that to fix bug #1545123
- Disable controls and prevent navigation while a delayed/timed shoot is ongoing.
- Display a stock icon as the video thumbnail when thumbnailer fails (which it will do now if it can't process a video)
- Allow sharing multiple files, except if they are mixed content
- Only use full screen in staged mode.

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'GalleryView.qml'
--- GalleryView.qml 2016-02-23 11:46:52 +0000
+++ GalleryView.qml 2016-03-23 15:42:41 +0000
@@ -16,7 +16,7 @@
1616
17import QtQuick 2.417import QtQuick 2.4
18import Ubuntu.Components 1.318import Ubuntu.Components 1.3
19import Ubuntu.Content 0.119import Ubuntu.Content 1.3
20import Ubuntu.Thumbnailer 0.120import Ubuntu.Thumbnailer 0.1
21import CameraApp 0.121import CameraApp 0.1
22import "MimeTypeMapper.js" as MimeTypeMapper22import "MimeTypeMapper.js" as MimeTypeMapper
@@ -35,7 +35,7 @@
35 StorageLocations.removableStorageVideosLocation]35 StorageLocations.removableStorageVideosLocation]
36 typeFilters: !main.contentExportMode ? [ "image", "video" ]36 typeFilters: !main.contentExportMode ? [ "image", "video" ]
37 : [MimeTypeMapper.contentTypeToMimeType(main.transferContentType)]37 : [MimeTypeMapper.contentTypeToMimeType(main.transferContentType)]
38 singleSelectionOnly: main.transfer.selectionType === ContentTransfer.Single38 singleSelectionOnly: main.contentExportMode && main.transfer.selectionType === ContentTransfer.Single
39 }39 }
4040
41 property bool gridMode: main.contentExportMode41 property bool gridMode: main.contentExportMode
@@ -58,7 +58,6 @@
5858
59 function exitUserSelectionMode() {59 function exitUserSelectionMode() {
60 model.clearSelection();60 model.clearSelection();
61 model.singleSelectionOnly = true;
62 userSelectionMode = false;61 userSelectionMode = false;
63 }62 }
6463
@@ -76,6 +75,7 @@
76 model: galleryView.model75 model: galleryView.model
77 visible: opacity != 0.076 visible: opacity != 0.0
78 inView: galleryView.inView && galleryView.currentView == slideshowView77 inView: galleryView.inView && galleryView.currentView == slideshowView
78 focus: inView
79 inSelectionMode: main.contentExportMode || galleryView.userSelectionMode79 inSelectionMode: main.contentExportMode || galleryView.userSelectionMode
80 onToggleSelection: model.toggleSelected(currentIndex)80 onToggleSelection: model.toggleSelected(currentIndex)
81 onToggleHeader: header.toggle();81 onToggleHeader: header.toggle();
@@ -89,6 +89,7 @@
89 model: galleryView.model89 model: galleryView.model
90 visible: opacity != 0.090 visible: opacity != 0.0
91 inView: galleryView.inView && galleryView.currentView == photogridView91 inView: galleryView.inView && galleryView.currentView == photogridView
92 focus: inView
92 inSelectionMode: main.contentExportMode || galleryView.userSelectionMode93 inSelectionMode: main.contentExportMode || galleryView.userSelectionMode
93 onPhotoClicked: {94 onPhotoClicked: {
94 slideshowView.showPhotoAtIndex(index);95 slideshowView.showPhotoAtIndex(index);
@@ -97,7 +98,6 @@
97 onPhotoPressAndHold: {98 onPhotoPressAndHold: {
98 if (!galleryView.userSelectionMode) {99 if (!galleryView.userSelectionMode) {
99 galleryView.userSelectionMode = true;100 galleryView.userSelectionMode = true;
100 model.singleSelectionOnly = false;
101 model.toggleSelected(index);101 model.toggleSelected(index);
102 }102 }
103 }103 }
104104
=== modified file 'GalleryViewHeader.qml'
--- GalleryViewHeader.qml 2016-02-25 13:01:05 +0000
+++ GalleryViewHeader.qml 2016-03-23 15:42:41 +0000
@@ -153,6 +153,7 @@
153153
154 Item {154 Item {
155 id: actionsDrawer155 id: actionsDrawer
156 objectName: "actionsDrawer"
156157
157 anchors {158 anchors {
158 top: parent.bottom159 top: parent.bottom
@@ -170,6 +171,8 @@
170 }171 }
171172
172 property bool opened: false173 property bool opened: false
174 property bool fullyOpened: actionsColumn.y == 0
175 property bool fullyClosed: actionsColumn.y == -actionsColumn.height
173 property list<Action> actions176 property list<Action> actions
174177
175 onOpenedChanged: {178 onOpenedChanged: {
176179
=== modified file 'MimeTypeMapper.js'
--- MimeTypeMapper.js 2014-07-31 18:41:17 +0000
+++ MimeTypeMapper.js 2016-03-23 15:42:41 +0000
@@ -17,7 +17,7 @@
17*/17*/
1818
19.pragma library19.pragma library
20.import Ubuntu.Content 0.1 as UbuntuContent20.import Ubuntu.Content 1.3 as UbuntuContent
2121
22function startsWith(string, prefix) {22function startsWith(string, prefix) {
23 return string.indexOf(prefix) === 0;23 return string.indexOf(prefix) === 0;
2424
=== modified file 'OptionButton.qml'
--- OptionButton.qml 2015-11-24 15:44:58 +0000
+++ OptionButton.qml 2016-03-23 15:42:41 +0000
@@ -26,8 +26,8 @@
26 iconName: !model.get(model.selectedIndex).icon ? model.icon : model.get(model.selectedIndex).icon26 iconName: !model.get(model.selectedIndex).icon ? model.icon : model.get(model.selectedIndex).icon
27 iconSource: (model && model.iconSource) ? model.iconSource : ""27 iconSource: (model && model.iconSource) ? model.iconSource : ""
28 on: model.isToggle ? model.get(model.selectedIndex).value : true28 on: model.isToggle ? model.get(model.selectedIndex).value : true
29 enabled: model.available29 enabled: model.visible && model.available
30 label: model.label30 label: model.label
31 visible: model.visible31 visible: model.visible && model.available
32 automaticOrientation: false32 automaticOrientation: false
33}33}
3434
=== modified file 'PhotogridView.qml'
--- PhotogridView.qml 2016-02-23 11:46:52 +0000
+++ PhotogridView.qml 2016-03-23 15:42:41 +0000
@@ -18,11 +18,11 @@
18import Ubuntu.Components 1.318import Ubuntu.Components 1.3
19import Ubuntu.Components.Popups 1.319import Ubuntu.Components.Popups 1.3
20import Ubuntu.Thumbnailer 0.120import Ubuntu.Thumbnailer 0.1
21import Ubuntu.Content 0.121import Ubuntu.Content 1.3
22import CameraApp 0.122import CameraApp 0.1
23import "MimeTypeMapper.js" as MimeTypeMapper23import "MimeTypeMapper.js" as MimeTypeMapper
2424
25Item {25FocusScope {
26 id: photogridView26 id: photogridView
2727
28 property var model28 property var model
@@ -40,11 +40,14 @@
40 Action {40 Action {
41 text: i18n.tr("Share")41 text: i18n.tr("Share")
42 iconName: "share"42 iconName: "share"
43 enabled: model.selectedFiles.length <= 143 enabled: model.selectedFiles.length > 0
44 onTriggered: {44 onTriggered: {
45 if (model.selectedFiles.length > 0) {45 // Display a warning message if we are attempting to share mixed
46 var dialog = PopupUtils.open(sharePopoverComponent)46 // content, as the framework does not properly support this
47 dialog.parent = photogridView47 if (selectionContainsMixedMedia()) {
48 PopupUtils.open(unableShareDialogComponent).parent = photogridView;
49 } else {
50 PopupUtils.open(sharePopoverComponent).parent = photogridView;
48 }51 }
49 }52 }
50 },53 },
@@ -60,6 +63,19 @@
60 }63 }
61 ]64 ]
6265
66 function selectionContainsMixedMedia() {
67 var selection = model.selectedFiles;
68 var lastType = model.get(selection[0], "fileType");
69 for (var i = 1; i < selection.length; i++) {
70 var type = model.get(selection[i], "fileType");
71 if (type !== lastType) {
72 return true;
73 }
74 lastType = type;
75 }
76 return false;
77 }
78
63 function showPhotoAtIndex(index) {79 function showPhotoAtIndex(index) {
64 gridView.positionViewAtIndex(index, GridView.Center);80 gridView.positionViewAtIndex(index, GridView.Center);
65 }81 }
@@ -139,6 +155,16 @@
139 visible: isVideo155 visible: isVideo
140 }156 }
141157
158 Icon {
159 objectName: "thumbnailLoadingErrorIcon"
160 anchors.centerIn: parent
161 width: units.gu(6)
162 height: width
163 name: cellDelegate.isVideo ? "stock_video" : "stock_image"
164 color: "white"
165 opacity: thumbnail.status == Image.Error ? 1.0 : 0.0
166 }
167
142 MouseArea {168 MouseArea {
143 anchors.fill: parent169 anchors.fill: parent
144 onClicked: photogridView.photoClicked(index)170 onClicked: photogridView.photoClicked(index)
@@ -160,6 +186,7 @@
160 visible: inSelectionMode186 visible: inSelectionMode
161187
162 Icon {188 Icon {
189 objectName: "mediaItemCheckBox"
163 anchors.centerIn: parent190 anchors.centerIn: parent
164 width: parent.width * 0.8191 width: parent.width * 0.8
165 height: parent.height * 0.8192 height: parent.height * 0.8
@@ -225,4 +252,13 @@
225 onVisibleChanged: photogridView.toggleHeader()252 onVisibleChanged: photogridView.toggleHeader()
226 }253 }
227 }254 }
255
256 Component {
257 id: unableShareDialogComponent
258 UnableShareDialog {
259 objectName: "unableShareDialog"
260 onVisibleChanged: photogridView.toggleHeader()
261 }
262 }
263
228}264}
229265
=== modified file 'SlideshowView.qml'
--- SlideshowView.qml 2016-02-23 11:46:52 +0000
+++ SlideshowView.qml 2016-03-23 15:42:41 +0000
@@ -18,12 +18,12 @@
18import Ubuntu.Components 1.318import Ubuntu.Components 1.3
19import Ubuntu.Components.ListItems 1.3 as ListItems19import Ubuntu.Components.ListItems 1.3 as ListItems
20import Ubuntu.Components.Popups 1.320import Ubuntu.Components.Popups 1.3
21import Ubuntu.Content 0.121import Ubuntu.Content 1.3
22import Ubuntu.Thumbnailer 0.122import Ubuntu.Thumbnailer 0.1
23import CameraApp 0.123import CameraApp 0.1
24import "MimeTypeMapper.js" as MimeTypeMapper24import "MimeTypeMapper.js" as MimeTypeMapper
2525
26Item {26FocusScope {
27 id: slideshowView27 id: slideshowView
2828
29 property var model29 property var model
@@ -111,14 +111,12 @@
111111
112 anchors.fill: parent112 anchors.fill: parent
113 model: slideshowView.model113 model: slideshowView.model
114 focus: true
114 orientation: ListView.Horizontal115 orientation: ListView.Horizontal
115 boundsBehavior: Flickable.StopAtBounds116 boundsBehavior: Flickable.StopAtBounds
116 cacheBuffer: width117 cacheBuffer: width
117 highlightRangeMode: ListView.StrictlyEnforceRange118 highlightRangeMode: ListView.StrictlyEnforceRange
118 // FIXME: this disables the animation introduced by highlightRangeMode119 highlightMoveDuration: UbuntuAnimation.FastDuration
119 // happening setting currentIndex; it is necessary at least because we
120 // were hitting https://bugreports.qt-project.org/browse/QTBUG-41035
121 highlightMoveDuration: 0
122 snapMode: ListView.SnapOneItem120 snapMode: ListView.SnapOneItem
123 onCountChanged: {121 onCountChanged: {
124 // currentIndex is -1 by default and stays so until manually set to something else122 // currentIndex is -1 by default and stays so until manually set to something else
@@ -140,6 +138,7 @@
140 }138 }
141 delegate: Item {139 delegate: Item {
142 id: delegate140 id: delegate
141 objectName: "mediaItem" + index
143 property bool pinchInProgress: zoomPinchArea.active142 property bool pinchInProgress: zoomPinchArea.active
144 property string url: fileURL143 property string url: fileURL
145 property bool isSelected: selected144 property bool isSelected: selected
@@ -183,6 +182,7 @@
183 property real maximumZoom: 3.0182 property real maximumZoom: 3.0
184 property bool active: false183 property bool active: false
185 property var center184 property var center
185 enabled: !media.isVideo
186186
187 onPinchStarted: {187 onPinchStarted: {
188 active = true;188 active = true;
@@ -258,6 +258,16 @@
258 }258 }
259 fillMode: Image.PreserveAspectFit259 fillMode: Image.PreserveAspectFit
260 }260 }
261
262 Icon {
263 objectName: "thumbnailLoadingErrorIcon"
264 anchors.centerIn: parent
265 width: units.gu(30)
266 height: width
267 name: media.isVideo ? "stock_video" : "stock_image"
268 color: "white"
269 opacity: image.status == Image.Error ? 1.0 : 0.0
270 }
261 }271 }
262272
263 Icon {273 Icon {
264274
=== added file 'UnableShareDialog.qml'
--- UnableShareDialog.qml 1970-01-01 00:00:00 +0000
+++ UnableShareDialog.qml 2016-03-23 15:42:41 +0000
@@ -0,0 +1,34 @@
1/*
2 * Copyright (C) 2016 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.4
18import Ubuntu.Components 1.3
19import Ubuntu.Components.Popups 1.3
20
21Dialog {
22 id: dialog
23 objectName: "unableShareDialog"
24
25 title: i18n.tr("Unable to share")
26 text: i18n.tr("Unable to share photos and videos at the same time")
27
28 Button {
29 objectName: "unableShareDialogOk"
30 text: i18n.tr("Ok")
31 color: UbuntuColors.orange
32 onClicked: PopupUtils.close(dialog);
33 }
34}
035
=== modified file 'ViewFinderOverlay.qml'
--- ViewFinderOverlay.qml 2016-02-23 11:46:52 +0000
+++ ViewFinderOverlay.qml 2016-03-23 15:42:41 +0000
@@ -33,6 +33,7 @@
33 property var controls: controls33 property var controls: controls
34 property var settings: settings34 property var settings: settings
35 property bool readyForCapture35 property bool readyForCapture
36 property int sensorOrientation
3637
37 function showFocusRing(x, y) {38 function showFocusRing(x, y) {
38 focusRing.center = Qt.point(x, y);39 focusRing.center = Qt.point(x, y);
@@ -289,6 +290,7 @@
289 id: bottomEdgeClose290 id: bottomEdgeClose
290 anchors.fill: parent291 anchors.fill: parent
291 onClicked: optionsOverlayClose()292 onClicked: optionsOverlayClose()
293 enabled: !camera.timedCaptureInProgress
292 }294 }
293295
294 OrientationHelper {296 OrientationHelper {
@@ -305,8 +307,9 @@
305 height: optionsOverlayLoader.height307 height: optionsOverlayLoader.height
306 onOpenedChanged: optionsOverlayLoader.item.closeValueSelector()308 onOpenedChanged: optionsOverlayLoader.item.closeValueSelector()
307 enabled: camera.videoRecorder.recorderState == CameraRecorder.StoppedState309 enabled: camera.videoRecorder.recorderState == CameraRecorder.StoppedState
308 && !camera.photoCaptureInProgress310 && !camera.photoCaptureInProgress && !camera.timedCaptureInProgress
309 opacity: enabled ? 1.0 : 0.3311 opacity: enabled ? 1.0 : 0.3
312 property bool ready: optionsOverlayLoader.status == Loader.Ready
310313
311 /* At startup, opened is false and 'bottomEdge.height' is 0 until314 /* At startup, opened is false and 'bottomEdge.height' is 0 until
312 optionsOverlayLoader has finished loading. When that happens315 optionsOverlayLoader has finished loading. When that happens
@@ -652,13 +655,15 @@
652 enabled: visible655 enabled: visible
653656
654 function timedShoot(secs) {657 function timedShoot(secs) {
658 camera.timedCaptureInProgress = true;
655 timedShootFeedback.start();659 timedShootFeedback.start();
656 shootingTimer.remainingSecs = secs;660 shootingTimer.remainingSecs = secs;
657 shootingTimer.start();661 shootingTimer.start();
658 }662 }
659663
660 function cancelTimedShoot() {664 function cancelTimedShoot() {
661 if (shootingTimer.running) {665 if (camera.timedCaptureInProgress) {
666 camera.timedCaptureInProgress = false;
662 shootingTimer.stop();667 shootingTimer.stop();
663 timedShootFeedback.stop();668 timedShootFeedback.stop();
664 }669 }
@@ -692,9 +697,8 @@
692 break;697 break;
693 }698 }
694699
695 if (Screen.primaryOrientation == Qt.PortraitOrientation) {700 // account for the orientation of the sensor
696 orientation += 90;701 orientation -= viewFinderOverlay.sensorOrientation;
697 }
698702
699 if (camera.captureMode == Camera.CaptureVideo) {703 if (camera.captureMode == Camera.CaptureVideo) {
700 if (main.contentExportMode) {704 if (main.contentExportMode) {
@@ -774,6 +778,7 @@
774 onTriggered: {778 onTriggered: {
775 if (remainingSecs == 0) {779 if (remainingSecs == 0) {
776 running = false;780 running = false;
781 camera.timedCaptureInProgress = false;
777 controls.shoot();782 controls.shoot();
778 timedShootFeedback.stop();783 timedShootFeedback.stop();
779 } else {784 } else {
@@ -819,7 +824,7 @@
819 iconName: (camera.captureMode == Camera.CaptureStillImage) ? "camcorder" : "camera-symbolic"824 iconName: (camera.captureMode == Camera.CaptureStillImage) ? "camcorder" : "camera-symbolic"
820 onClicked: controls.changeRecordMode()825 onClicked: controls.changeRecordMode()
821 enabled: camera.videoRecorder.recorderState == CameraRecorder.StoppedState && !main.contentExportMode826 enabled: camera.videoRecorder.recorderState == CameraRecorder.StoppedState && !main.contentExportMode
822 && !camera.photoCaptureInProgress827 && !camera.photoCaptureInProgress && !camera.timedCaptureInProgress
823 }828 }
824829
825 ShootButton {830 ShootButton {
@@ -833,6 +838,7 @@
833 }838 }
834839
835 enabled: viewFinderOverlay.readyForCapture && !storageMonitor.diskSpaceCriticallyLow840 enabled: viewFinderOverlay.readyForCapture && !storageMonitor.diskSpaceCriticallyLow
841 && !camera.timedCaptureInProgress
836 state: (camera.captureMode == Camera.CaptureVideo) ?842 state: (camera.captureMode == Camera.CaptureVideo) ?
837 ((camera.videoRecorder.recorderState == CameraRecorder.StoppedState) ? "record_off" : "record_on") :843 ((camera.videoRecorder.recorderState == CameraRecorder.StoppedState) ? "record_off" : "record_on") :
838 "camera"844 "camera"
@@ -869,7 +875,7 @@
869 }875 }
870876
871 enabled: !camera.switchInProgress && camera.videoRecorder.recorderState == CameraRecorder.StoppedState877 enabled: !camera.switchInProgress && camera.videoRecorder.recorderState == CameraRecorder.StoppedState
872 && !camera.photoCaptureInProgress878 && !camera.photoCaptureInProgress && !camera.timedCaptureInProgress
873 iconName: "camera-flip"879 iconName: "camera-flip"
874 onClicked: controls.switchCamera()880 onClicked: controls.switchCamera()
875 }881 }
@@ -892,7 +898,7 @@
892 property real maximumScale: 3.0898 property real maximumScale: 3.0
893 property bool active: false899 property bool active: false
894900
895 enabled: !camera.photoCaptureInProgress901 enabled: !camera.photoCaptureInProgress && !camera.timedCaptureInProgress
896 onPinchStarted: {902 onPinchStarted: {
897 active = true;903 active = true;
898 initialZoom = zoomControl.value;904 initialZoom = zoomControl.value;
@@ -910,6 +916,7 @@
910916
911 MouseArea {917 MouseArea {
912 id: manualFocusMouseArea918 id: manualFocusMouseArea
919 objectName: "manualFocusMouseArea"
913 anchors {920 anchors {
914 fill: parent921 fill: parent
915 // Pinch gestures need more clearance at the edges of the screen, but922 // Pinch gestures need more clearance at the edges of the screen, but
@@ -918,7 +925,7 @@
918 rightMargin: -bottomEdgeIndicators.height925 rightMargin: -bottomEdgeIndicators.height
919 }926 }
920 enabled: camera.focus.isFocusPointModeSupported(Camera.FocusPointCustom) &&927 enabled: camera.focus.isFocusPointModeSupported(Camera.FocusPointCustom) &&
921 !camera.photoCaptureInProgress928 !camera.photoCaptureInProgress && !camera.timedCaptureInProgress
922 onClicked: {929 onClicked: {
923 camera.manualFocus(mouse.x, mouse.y);930 camera.manualFocus(mouse.x, mouse.y);
924 mouse.accepted = false;931 mouse.accepted = false;
925932
=== modified file 'ViewFinderOverlayLoader.qml'
--- ViewFinderOverlayLoader.qml 2016-02-23 11:46:52 +0000
+++ ViewFinderOverlayLoader.qml 2016-03-23 15:42:41 +0000
@@ -25,6 +25,7 @@
25 property var controls: loader.item ? loader.item.controls : null25 property var controls: loader.item ? loader.item.controls : null
26 property var settings: loader.item.settings26 property var settings: loader.item.settings
27 property bool readyForCapture27 property bool readyForCapture
28 property int sensorOrientation
2829
29 function showFocusRing(x, y) {30 function showFocusRing(x, y) {
30 loader.item.showFocusRing(x, y);31 loader.item.showFocusRing(x, y);
@@ -37,7 +38,8 @@
37 asynchronous: true38 asynchronous: true
38 Component.onCompleted: {39 Component.onCompleted: {
39 loader.setSource("ViewFinderOverlay.qml", { "camera": loader.camera,40 loader.setSource("ViewFinderOverlay.qml", { "camera": loader.camera,
40 "readyForCapture": Qt.binding(function() { return loader.readyForCapture})41 "readyForCapture": Qt.binding(function() { return loader.readyForCapture}),
42 "sensorOrientation": Qt.binding(function () {return loader.sensorOrientation})
41 });43 });
42 }44 }
43}45}
4446
=== modified file 'ViewFinderView.qml'
--- ViewFinderView.qml 2016-02-23 11:46:52 +0000
+++ ViewFinderView.qml 2016-03-23 15:42:41 +0000
@@ -21,9 +21,9 @@
21import QtMultimedia 5.021import QtMultimedia 5.0
22import CameraApp 0.122import CameraApp 0.1
23import QtGraphicalEffects 1.023import QtGraphicalEffects 1.0
24import Ubuntu.Content 0.124import Ubuntu.Content 1.3
2525
26Item {26FocusScope {
27 id: viewFinderView27 id: viewFinderView
2828
29 property bool overlayVisible: true29 property bool overlayVisible: true
@@ -60,11 +60,15 @@
60 property bool failedToConnect: false60 property bool failedToConnect: false
6161
62 function manualFocus(x, y) {62 function manualFocus(x, y) {
63 viewFinderOverlay.showFocusRing(x, y);63 var normalizedPoint = viewFinder.mapPointToSourceNormalized(Qt.point(x, y - viewFinder.y));
64 autoFocusTimer.restart();64 if (normalizedPoint.x >= 0.0 && normalizedPoint.x <= 1.0 &&
65 focus.focusMode = Camera.FocusAuto;65 normalizedPoint.y >= 0.0 && normalizedPoint.y <= 1.0) {
66 focus.customFocusPoint = viewFinder.mapPointToSourceNormalized(Qt.point(x, y));66 viewFinderOverlay.showFocusRing(x, y);
67 focus.focusPointMode = Camera.FocusPointCustom;67 autoFocusTimer.restart();
68 focus.focusMode = Camera.FocusAuto;
69 focus.customFocusPoint = normalizedPoint;
70 focus.focusPointMode = Camera.FocusPointCustom;
71 }
68 }72 }
6973
70 function autoFocus() {74 function autoFocus() {
@@ -96,6 +100,7 @@
96 property alias maximumZoom: camera.maximumDigitalZoom100 property alias maximumZoom: camera.maximumDigitalZoom
97 property bool switchInProgress: false101 property bool switchInProgress: false
98 property bool photoCaptureInProgress: false102 property bool photoCaptureInProgress: false
103 property bool timedCaptureInProgress: false
99104
100 onPhotoCaptureInProgressChanged: {105 onPhotoCaptureInProgressChanged: {
101 if (main.contentExportMode && camera.photoCaptureInProgress) {106 if (main.contentExportMode && camera.photoCaptureInProgress) {
@@ -110,14 +115,6 @@
110 if (photoRollHint.necessary && !main.transfer) photoRollHint.enable();115 if (photoRollHint.necessary && !main.transfer) photoRollHint.enable();
111 camera.photoCaptureInProgress = false;116 camera.photoCaptureInProgress = false;
112 }117 }
113
114 if (main.transfer) {
115 if (main.transfer.contentType === ContentType.Videos) {
116 viewFinderView.captureMode = Camera.CaptureVideo;
117 } else {
118 viewFinderView.captureMode = Camera.CaptureStillImage;
119 }
120 }
121 }118 }
122 }119 }
123120
@@ -265,6 +262,7 @@
265 // Set orientation only at startup because later on Screen.primaryOrientation262 // Set orientation only at startup because later on Screen.primaryOrientation
266 // may change.263 // may change.
267 orientation = Screen.primaryOrientation === Qt.PortraitOrientation ? -90 : 0;264 orientation = Screen.primaryOrientation === Qt.PortraitOrientation ? -90 : 0;
265 viewFinderOverlay.sensorOrientation = orientation;
268 }266 }
269267
270 transform: Rotation {268 transform: Rotation {
@@ -421,6 +419,7 @@
421419
422 PhotoRollHint {420 PhotoRollHint {
423 id: photoRollHint421 id: photoRollHint
422 objectName: "photoRollHint"
424 anchors.fill: parent423 anchors.fill: parent
425 visible: enabled424 visible: enabled
426425
427426
=== modified file 'camera-app.qml'
--- camera-app.qml 2016-02-23 11:46:52 +0000
+++ camera-app.qml 2016-03-23 15:42:41 +0000
@@ -20,7 +20,7 @@
20import Ubuntu.Components 1.320import Ubuntu.Components 1.3
21import Ubuntu.Unity.Action 1.1 as UnityActions21import Ubuntu.Unity.Action 1.1 as UnityActions
22import UserMetrics 0.122import UserMetrics 0.1
23import Ubuntu.Content 0.123import Ubuntu.Content 1.3
24import CameraApp 0.124import CameraApp 0.1
2525
26Window {26Window {
@@ -30,6 +30,26 @@
30 height: units.gu(80)30 height: units.gu(80)
31 color: "black"31 color: "black"
32 title: "Camera"32 title: "Camera"
33 // special flag only supported by Unity8/MIR so far that hides the shell's
34 // top panel in Staged mode
35 flags: Qt.Window | 0x00800000
36
37 property int preFullScreenVisibility
38
39 function toggleFullScreen() {
40 if (main.visibility != Window.FullScreen) {
41 preFullScreenVisibility = main.visibility;
42 main.visibility = Window.FullScreen;
43 } else {
44 main.visibility = preFullScreenVisibility;
45 }
46 }
47
48 function exitFullScreen() {
49 if (main.visibility == Window.FullScreen) {
50 main.visibility = preFullScreenVisibility;
51 }
52 }
3353
34 UnityActions.ActionManager {54 UnityActions.ActionManager {
35 actions: [55 actions: [
@@ -63,11 +83,7 @@
6383
64 Component.onCompleted: {84 Component.onCompleted: {
65 i18n.domain = "camera-app";85 i18n.domain = "camera-app";
66 if (!application.desktopMode) {86 main.show();
67 main.showFullScreen();
68 } else {
69 main.show();
70 }
71 }87 }
7288
7389
@@ -78,6 +94,15 @@
78 flickableDirection: state == "PORTRAIT" ? Flickable.HorizontalFlick : Flickable.VerticalFlick94 flickableDirection: state == "PORTRAIT" ? Flickable.HorizontalFlick : Flickable.VerticalFlick
79 boundsBehavior: Flickable.StopAtBounds95 boundsBehavior: Flickable.StopAtBounds
8096
97 Keys.onPressed: {
98 if (event.key == Qt.Key_F11) {
99 main.toggleFullScreen();
100 event.accepted = true;
101 }
102 }
103 Keys.onEscapePressed: main.exitFullScreen()
104
105
81 property real panesMargin: units.gu(1)106 property real panesMargin: units.gu(1)
82 property real ratio107 property real ratio
83 property int orientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)108 property int orientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
@@ -135,7 +160,9 @@
135 }160 }
136 }161 }
137 ]162 ]
138 interactive: !viewFinderView.touchAcquired && !galleryView.touchAcquired && !viewFinderView.camera.photoCaptureInProgress163 interactive: !viewFinderView.touchAcquired && !galleryView.touchAcquired
164 && !viewFinderView.camera.photoCaptureInProgress
165 && !viewFinderView.camera.timedCaptureInProgress
139166
140 Component.onCompleted: {167 Component.onCompleted: {
141 // FIXME: workaround for qtubuntu not returning values depending on the grid unit definition168 // FIXME: workaround for qtubuntu not returning values depending on the grid unit definition
@@ -256,6 +283,7 @@
256 height: viewSwitcher.height283 height: viewSwitcher.height
257 overlayVisible: !viewSwitcher.moving && !viewSwitcher.flicking284 overlayVisible: !viewSwitcher.moving && !viewSwitcher.flicking
258 inView: viewSwitcher.ratio < 0.5285 inView: viewSwitcher.ratio < 0.5
286 focus: !galleryView.focus
259 opacity: inView ? 1.0 : 0.0287 opacity: inView ? 1.0 : 0.0
260 onPhotoTaken: {288 onPhotoTaken: {
261 galleryView.prependMediaToModel(filePath);289 galleryView.prependMediaToModel(filePath);
@@ -273,6 +301,7 @@
273 width: viewSwitcher.width301 width: viewSwitcher.width
274 height: viewSwitcher.height302 height: viewSwitcher.height
275 inView: viewSwitcher.ratio > 0.0303 inView: viewSwitcher.ratio > 0.0
304 focus: inView
276 onExit: viewSwitcher.switchToViewFinder()305 onExit: viewSwitcher.switchToViewFinder()
277 opacity: inView ? 1.0 : 0.0306 opacity: inView ? 1.0 : 0.0
278 }307 }
@@ -280,7 +309,7 @@
280309
281 property bool contentExportMode: transfer !== null310 property bool contentExportMode: transfer !== null
282 property var transfer: null311 property var transfer: null
283 property var transferContentType: ContentType.Pictures312 property var transferContentType: transfer ? transfer.contentType : "image"
284313
285 function exportContent(urls) {314 function exportContent(urls) {
286 if (!main.transfer) return;315 if (!main.transfer) return;
@@ -312,16 +341,10 @@
312 onExportRequested: {341 onExportRequested: {
313 viewSwitcher.switchToViewFinder();342 viewSwitcher.switchToViewFinder();
314343
315 // The exportRequested event can arrive before or after the344 if (transfer.contentType === ContentType.Videos) {
316 // app is active, but setting the recording type before the345 viewFinderView.captureMode = Camera.CaptureVideo;
317 // capture becomes ready does not have any effect.346 } else {
318 // See camera.imageCapture.onReadyChanged for the other case.347 viewFinderView.captureMode = Camera.CaptureStillImage;
319 if (viewFinderView.camera.imageCapture.ready) {
320 if (transfer.contentType === ContentType.Videos) {
321 viewFinderView.captureMode = Camera.CaptureVideo;
322 } else {
323 viewFinderView.captureMode = Camera.CaptureStillImage;
324 }
325 }348 }
326 main.transfer = transfer;349 main.transfer = transfer;
327 }350 }
328351
=== modified file 'debian/control'
--- debian/control 2015-07-31 02:23:54 +0000
+++ debian/control 2016-03-23 15:42:41 +0000
@@ -13,11 +13,14 @@
13 qtbase5-dev,13 qtbase5-dev,
14 qtdeclarative5-dev,14 qtdeclarative5-dev,
15 qml-module-qtquick2,15 qml-module-qtquick2,
16 qml-module-qtpositioning,
16 qml-module-qttest,17 qml-module-qttest,
17 qtdeclarative5-ubuntu-ui-toolkit-plugin,18 qtdeclarative5-ubuntu-ui-toolkit-plugin,
18 qtdeclarative5-unity-action-plugin (>= 1.1.0),19 qtdeclarative5-unity-action-plugin (>= 1.1.0),
19 qtdeclarative5-usermetrics0.1,20 qtdeclarative5-usermetrics0.1,
20 qtdeclarative5-ubuntu-content1,21 qtdeclarative5-ubuntu-content1,
22 qtdeclarative5-ubuntu-thumbnailer0.1,
23 qtdeclarative5-ubuntu-ui-extras0.2,
21 qtmultimedia5-dev,24 qtmultimedia5-dev,
22 libusermetricsinput1-dev,25 libusermetricsinput1-dev,
23 gettext,26 gettext,
2427
=== modified file 'tests/autopilot/camera_app/emulators/main_window.py'
--- tests/autopilot/camera_app/emulators/main_window.py 2015-11-25 17:00:31 +0000
+++ tests/autopilot/camera_app/emulators/main_window.py 2016-03-23 15:42:41 +0000
@@ -36,10 +36,29 @@
36 """Returns the gallery view"""36 """Returns the gallery view"""
37 return self.app.wait_select_single("GalleryView")37 return self.app.wait_select_single("GalleryView")
3838
39 def get_media(self, index=0):
40 """Returns media at index in the currently loaded view in gallery"""
41 gallery = self.get_gallery()
42 view = gallery.select_single("SlideshowView")
43 if not view.visible:
44 view = gallery.select_single("PhotogridView")
45
46 return view.wait_select_single(objectName="mediaItem" + str(index))
47
48 def get_broken_media_icon(self, index=0):
49 """Returns the broken media icon"""
50 media = self.get_media(index)
51 return media.wait_select_single(objectName="thumbnailLoadingErrorIcon")
52
39 def get_no_media_hint(self):53 def get_no_media_hint(self):
40 """Returns the Item representing the hint that no media is available"""54 """Returns the Item representing the hint that no media is available"""
41 return self.app.wait_select_single(objectName="noMediaHint")55 return self.app.wait_select_single(objectName="noMediaHint")
4256
57 def get_focus_mouse_area(self):
58 """Returns the focus mouse area"""
59 return self.app.wait_select_single("QQuickMouseArea",
60 objectName="manualFocusMouseArea")
61
43 def get_focus_ring(self):62 def get_focus_ring(self):
44 """Returns the focus ring of the camera"""63 """Returns the focus ring of the camera"""
45 return self.app.wait_select_single("FocusRing")64 return self.app.wait_select_single("FocusRing")
@@ -49,9 +68,9 @@
49 return self.app.wait_select_single("ShootButton")68 return self.app.wait_select_single("ShootButton")
5069
51 def get_photo_roll_hint(self):70 def get_photo_roll_hint(self):
52 """Returns the layer that serves at hinting to the existence of the71 """Returns the photo roll hint"""
53 photo roll"""72 return self.app.wait_select_single("PhotoRollHint",
54 return self.app.wait_select_single("PhotoRollHint")73 objectName="photoRollHint")
5574
56 def get_record_control(self):75 def get_record_control(self):
57 """Returns the button that toggles between photo and video recording"""76 """Returns the button that toggles between photo and video recording"""
@@ -63,8 +82,12 @@
63 in settingsProperty82 in settingsProperty
64 """83 """
65 optionButtons = self.app.select_many("OptionButton")84 optionButtons = self.app.select_many("OptionButton")
66 return next(button for button in optionButtons85 optionButton = next(button for button in optionButtons
67 if button.settingsProperty == settingsProperty)86 if button.settingsProperty == settingsProperty)
87 if optionButton.visible:
88 return optionButton
89 else:
90 return None
6891
69 def get_flash_button(self):92 def get_flash_button(self):
70 """Returns the flash control button of the camera"""93 """Returns the flash control button of the camera"""
@@ -90,6 +113,10 @@
90 """Returns the video resolution button of the camera"""113 """Returns the video resolution button of the camera"""
91 return self.get_option_button("videoResolution")114 return self.get_option_button("videoResolution")
92115
116 def get_timer_delay_button(self):
117 """Returns the timer delay option button of the camera"""
118 return self.get_option_button("selfTimerDelay")
119
93 def get_stop_watch(self):120 def get_stop_watch(self):
94 """Returns the stop watch when using the record button of the camera"""121 """Returns the stop watch when using the record button of the camera"""
95 return self.app.wait_select_single("StopWatch")122 return self.app.wait_select_single("StopWatch")
@@ -139,30 +166,71 @@
139 except:166 except:
140 return None167 return None
141168
169 def open_actions_drawer(self, gallery):
170 """Opens action drawer of gallery"""
171 actionsDrawerButton = gallery.wait_select_single(
172 "IconButton",
173 objectName="additionalActionsButton")
174 self.app.pointing_device.move_to_object(actionsDrawerButton)
175 self.app.pointing_device.click()
176 actionsDrawer = gallery.wait_select_single("QQuickItem",
177 objectName="actionsDrawer")
178 actionsDrawer.fullyOpened.wait_for(True)
179
180 def close_actions_drawer(self, gallery):
181 """Closes action drawer of gallery"""
182 actionsDrawerButton = gallery.wait_select_single(
183 "IconButton",
184 objectName="additionalActionsButton")
185 self.app.pointing_device.move_to_object(actionsDrawerButton)
186 self.app.pointing_device.click()
187 actionsDrawer = gallery.wait_select_single("QQuickItem",
188 objectName="actionsDrawer")
189 actionsDrawer.fullyClosed.wait_for(True)
190
142 def swipe_to_gallery(self, testCase):191 def swipe_to_gallery(self, testCase):
143 view_switcher = self.get_view_switcher()192 view_switcher = self.get_view_switcher()
193 viewfinder = self.get_viewfinder()
194 view_switcher.interactive.wait_for(True)
195 view_switcher.enabled.wait_for(True)
196 view_switcher.settling.wait_for(False)
197 view_switcher.switching.wait_for(False)
198 viewfinder.inView.wait_for(True)
144 x, y = view_switcher.x, view_switcher.y199 x, y = view_switcher.x, view_switcher.y
145 w, h = view_switcher.width, view_switcher.height200 w, h = view_switcher.width, view_switcher.height
146201
147 tx = x + (w // 2)202 tx = x + (w // 2)
148 ty = y + (h // 2)203 ty = y + (h // 2)
149204
150 self.app.pointing_device.drag(tx, ty, x, ty, rate=1)205 # FIXME: a rate higher than 1 does not always make view_switcher move
151 viewfinder = self.get_viewfinder()206 self.app.pointing_device.drag(tx, ty, x, ty, rate=1,
207 time_between_events=0.0001)
208
152 testCase.assertThat(viewfinder.inView, Eventually(Equals(False)))209 testCase.assertThat(viewfinder.inView, Eventually(Equals(False)))
210 view_switcher.settling.wait_for(False)
211 view_switcher.switching.wait_for(False)
153212
154 def swipe_to_viewfinder(self, testCase):213 def swipe_to_viewfinder(self, testCase):
155 view_switcher = self.get_view_switcher()214 view_switcher = self.get_view_switcher()
215 viewfinder = self.get_viewfinder()
216 view_switcher.interactive.wait_for(True)
217 view_switcher.enabled.wait_for(True)
218 view_switcher.settling.wait_for(False)
219 view_switcher.switching.wait_for(False)
220 viewfinder.inView.wait_for(False)
156 x, y = view_switcher.x, view_switcher.y221 x, y = view_switcher.x, view_switcher.y
157 w, h = view_switcher.width, view_switcher.height222 w, h = view_switcher.width, view_switcher.height
158223
159 tx = x + (w // 2)224 tx = x + (w // 2)
160 ty = y + (h // 2)225 ty = y + (h // 2)
161226
227 # FIXME: a rate higher than 1 does not always make view_switcher move
162 self.app.pointing_device.drag(228 self.app.pointing_device.drag(
163 tx, ty, (tx + view_switcher.width // 2), ty, rate=1)229 tx, ty, (tx + view_switcher.width // 2), ty, rate=1,
164 viewfinder = self.get_viewfinder()230 time_between_events=0.0001)
165 testCase.assertThat(viewfinder.inView, Eventually(Equals(True)))231 testCase.assertThat(viewfinder.inView, Eventually(Equals(True)))
232 view_switcher.settling.wait_for(False)
233 view_switcher.switching.wait_for(False)
166234
167 def switch_cameras(self):235 def switch_cameras(self):
168 # Swap cameras and wait for camera to settle236 # Swap cameras and wait for camera to settle
169237
=== modified file 'tests/autopilot/camera_app/emulators/panel.py'
--- tests/autopilot/camera_app/emulators/panel.py 2015-12-01 09:03:34 +0000
+++ tests/autopilot/camera_app/emulators/panel.py 2016-03-23 15:42:41 +0000
@@ -24,6 +24,7 @@
24 :return: The panel.24 :return: The panel.
2525
26 """26 """
27 self.ready.wait_for(True)
27 self.animating.wait_for(False)28 self.animating.wait_for(False)
28 if not self.opened:29 if not self.opened:
29 self._drag_to_open()30 self._drag_to_open()
@@ -38,11 +39,14 @@
38 start_y = y + self.height - 139 start_y = y + self.height - 1
39 stop_y = y40 stop_y = y
4041
41 self.pointing_device.drag(line_x, start_y, line_x, stop_y)42 # FIXME: a rate higher than 1 does not always make panel move
43 self.pointing_device.drag(line_x, start_y, line_x, stop_y, rate=1,
44 time_between_events=0.0001)
4245
43 @autopilot_logging.log_action(logger.info)46 @autopilot_logging.log_action(logger.info)
44 def close(self):47 def close(self):
45 """Close the panel if it's opened."""48 """Close the panel if it's opened."""
49 self.ready.wait_for(True)
46 self.animating.wait_for(False)50 self.animating.wait_for(False)
47 if self.opened:51 if self.opened:
48 self._drag_to_close()52 self._drag_to_close()
@@ -55,4 +59,6 @@
55 start_y = y59 start_y = y
56 stop_y = y + self.height - 160 stop_y = y + self.height - 1
5761
58 self.pointing_device.drag(line_x, start_y, line_x, stop_y)62 # FIXME: a rate higher than 1 does not always make panel move
63 self.pointing_device.drag(line_x, start_y, line_x, stop_y, rate=1,
64 time_between_events=0.0001)
5965
=== modified file 'tests/autopilot/camera_app/tests/__init__.py'
--- tests/autopilot/camera_app/tests/__init__.py 2015-05-15 07:29:16 +0000
+++ tests/autopilot/camera_app/tests/__init__.py 2016-03-23 15:42:41 +0000
@@ -8,7 +8,6 @@
8"""Camera-app autopilot tests."""8"""Camera-app autopilot tests."""
99
10import os10import os
11import time
12import shutil11import shutil
13from pkg_resources import resource_filename12from pkg_resources import resource_filename
1413
@@ -42,6 +41,12 @@
42 sample_dir = resource_filename('camera_app', 'data')41 sample_dir = resource_filename('camera_app', 'data')
4342
44 def setUp(self):43 def setUp(self):
44 # Remove configuration file
45 config_file = os.path.expanduser(
46 "~/.config/com.ubuntu.camera/com.ubuntu.camera.conf")
47 if os.path.exists(config_file):
48 os.remove(config_file)
49
45 self.pointing_device = Pointer(self.input_device_class.create())50 self.pointing_device = Pointer(self.input_device_class.create())
46 super(CameraAppTestCase, self).setUp()51 super(CameraAppTestCase, self).setUp()
47 if os.path.exists(self.local_location):52 if os.path.exists(self.local_location):
@@ -51,11 +56,7 @@
51 else:56 else:
52 self.launch_click_installed()57 self.launch_click_installed()
5358
54 # wait and sleep as workaround for bug #1373039. To
55 # make sure large components get loaded asynchronously on start-up
56 # -- Chris Gagnon 11-17-2014
57 self.main_window.get_qml_view().visible.wait_for(True)59 self.main_window.get_qml_view().visible.wait_for(True)
58 time.sleep(5)
5960
60 def launch_test_local(self):61 def launch_test_local(self):
61 self.app = self.launch_test_application(62 self.app = self.launch_test_application(
@@ -107,6 +108,11 @@
107 shutil.copyfile(os.path.join(self.sample_dir, "sample.jpg"),108 shutil.copyfile(os.path.join(self.sample_dir, "sample.jpg"),
108 os.path.join(self.pictures_dir, "sample.jpg"))109 os.path.join(self.pictures_dir, "sample.jpg"))
109110
110 def add_sample_video(self):111 def add_sample_video(self, broken=False):
111 shutil.copyfile(os.path.join(self.sample_dir, "sample.mp4"),112 if broken:
112 os.path.join(self.videos_dir, "sample.mp4"))113 path = os.path.join(self.videos_dir, "sample_broken.mp4")
114 with open(path, "w") as video:
115 video.write("I AM NOT A VIDEO")
116 else:
117 shutil.copyfile(os.path.join(self.sample_dir, "sample.mp4"),
118 os.path.join(self.videos_dir, "sample.mp4"))
113119
=== modified file 'tests/autopilot/camera_app/tests/test_capture.py'
--- tests/autopilot/camera_app/tests/test_capture.py 2015-12-01 09:03:34 +0000
+++ tests/autopilot/camera_app/tests/test_capture.py 2016-03-23 15:42:41 +0000
@@ -17,6 +17,7 @@
17import unittest17import unittest
18import time18import time
19import os19import os
20import glob
2021
2122
22class TestCapture(CameraAppTestCase):23class TestCapture(CameraAppTestCase):
@@ -25,23 +26,10 @@
25 """ This is needed to wait for the application to start.26 """ This is needed to wait for the application to start.
26 In the testfarm, the application may take some time to show up."""27 In the testfarm, the application may take some time to show up."""
27 def setUp(self):28 def setUp(self):
28 # Remove configuration file where knowledge of the photo roll hint's
29 # necessity is stored
30 config_file = os.path.expanduser(
31 "~/.config/com.ubuntu.camera/com.ubuntu.camera.conf")
32 if os.path.exists(config_file):
33 os.remove(config_file)
34
35 super(TestCapture, self).setUp()29 super(TestCapture, self).setUp()
36
37 self.assertThat(
38 self.main_window.get_qml_view().visible, Eventually(Equals(True)))
39 self.pictures_dir = os.path.expanduser("~/Pictures/com.ubuntu.camera")30 self.pictures_dir = os.path.expanduser("~/Pictures/com.ubuntu.camera")
40 self.videos_dir = os.path.expanduser("~/Videos/com.ubuntu.camera")31 self.videos_dir = os.path.expanduser("~/Videos/com.ubuntu.camera")
4132
42 def tearDown(self):
43 super(TestCapture, self).tearDown()
44
45 """Test taking a picture"""33 """Test taking a picture"""
46 def test_take_picture(self):34 def test_take_picture(self):
47 exposure_button = self.main_window.get_exposure_button()35 exposure_button = self.main_window.get_exposure_button()
@@ -83,6 +71,62 @@
83 # check that the camera is able to capture another photo71 # check that the camera is able to capture another photo
84 self.assertThat(exposure_button.enabled, Eventually(Equals(True)))72 self.assertThat(exposure_button.enabled, Eventually(Equals(True)))
8573
74 """Test taking a picture with a timer set"""
75 def test_take_picture_with_timer(self):
76 delay = 5
77 self.enable_timer("%s seconds" % str(delay))
78
79 # start timed shoot
80 shoot_button = self.main_window.get_exposure_button()
81 self.assertThat(shoot_button.enabled, Eventually(Equals(True)))
82 self.pointing_device.move_to_object(shoot_button)
83 self.pointing_device.click()
84
85 switch_cameras_button = self.main_window.get_swap_camera_button()
86 record_mode_button = self.main_window.get_record_control()
87 view_switcher = self.main_window.get_view_switcher()
88
89 # controls and navigation should be disabled at this point
90 self.assertThat(shoot_button.enabled,
91 Eventually(Equals(True)))
92 self.assertThat(switch_cameras_button.enabled,
93 Eventually(Equals(True)))
94 self.assertThat(record_mode_button.enabled,
95 Eventually(Equals(True)))
96 self.assertThat(view_switcher.interactive,
97 Eventually(Equals(True)))
98
99 # after the delay controls and navigation should be re-enabled
100 self.assertThat(shoot_button.enabled,
101 Eventually(Equals(True), timeout=delay))
102 self.assertThat(switch_cameras_button.enabled,
103 Eventually(Equals(True), timeout=delay))
104 self.assertThat(record_mode_button.enabled,
105 Eventually(Equals(True), timeout=delay))
106 self.assertThat(view_switcher.interactive,
107 Eventually(Equals(True), timeout=delay))
108
109 def enable_timer(self, label_value):
110 # open bottom edge
111 bottom_edge = self.main_window.get_bottom_edge()
112 bottom_edge.open()
113
114 # open video resolution option value selector showing the possible
115 # values
116 timer_delay_button = self.main_window.get_timer_delay_button()
117 self.pointing_device.move_to_object(timer_delay_button)
118 self.pointing_device.click()
119 option_value_selector = self.main_window.get_option_value_selector()
120 self.assertThat(
121 option_value_selector.visible, Eventually(Equals(True)))
122
123 # select a 5 seconds delay
124 option = self.main_window.get_option_value_button(label_value)
125 self.pointing_device.move_to_object(option)
126 self.pointing_device.click()
127
128 bottom_edge.close()
129
86 def test_record_video(self):130 def test_record_video(self):
87 """Test clicking on the record control.131 """Test clicking on the record control.
88132
@@ -208,7 +252,7 @@
208 def get_first_picture(self, timeout=10):252 def get_first_picture(self, timeout=10):
209 pictures = []253 pictures = []
210 for i in range(0, timeout):254 for i in range(0, timeout):
211 pictures = os.listdir(self.pictures_dir)255 pictures = glob.glob(os.path.join(self.pictures_dir, "*.jpg"))
212 if len(pictures) != 0:256 if len(pictures) != 0:
213 break257 break
214 time.sleep(1)258 time.sleep(1)
@@ -236,9 +280,11 @@
236 return quality280 return quality
237281
238 def dismiss_first_photo_hint(self):282 def dismiss_first_photo_hint(self):
239 # Swipe to photo roll and back to viewfinder283 photo_roll_hint = self.main_window.get_photo_roll_hint()
240 self.main_window.swipe_to_gallery(self)284 if photo_roll_hint.enabled:
241 self.main_window.swipe_to_viewfinder(self)285 # Swipe to photo roll and back to viewfinder
286 self.main_window.swipe_to_gallery(self)
287 self.main_window.swipe_to_viewfinder(self)
242288
243 def set_compression_quality(self, quality="Normal Quality"):289 def set_compression_quality(self, quality="Normal Quality"):
244 # open bottom edge290 # open bottom edge
@@ -276,9 +322,10 @@
276 # switch cameras and select the last resolution for the current camera322 # switch cameras and select the last resolution for the current camera
277 self.main_window.switch_cameras()323 self.main_window.switch_cameras()
278 resolutions = self.get_available_video_resolutions()324 resolutions = self.get_available_video_resolutions()
279 expected_resolution = resolutions[-1]325 if len(resolutions) > 1:
280 self.assertThat(expected_resolution, NotEquals(initial_resolution))326 expected_resolution = resolutions[-1]
281 self.set_video_resolution(expected_resolution)327 self.assertThat(expected_resolution, NotEquals(initial_resolution))
328 self.set_video_resolution(expected_resolution)
282329
283 # switch back to the initial camera and record a video330 # switch back to the initial camera and record a video
284 self.main_window.switch_cameras()331 self.main_window.switch_cameras()
285332
=== modified file 'tests/autopilot/camera_app/tests/test_diskspace.py'
--- tests/autopilot/camera_app/tests/test_diskspace.py 2015-04-29 15:56:44 +0000
+++ tests/autopilot/camera_app/tests/test_diskspace.py 2016-03-23 15:42:41 +0000
@@ -61,9 +61,6 @@
61 # threshold as they all expect a normal situation at the start61 # threshold as they all expect a normal situation at the start
62 self.assertThat(self.diskSpaceAvailable(), GreaterThan(LOW_THRESHOLD))62 self.assertThat(self.diskSpaceAvailable(), GreaterThan(LOW_THRESHOLD))
6363
64 self.assertThat(
65 self.main_window.get_qml_view().visible, Eventually(Equals(True)))
66
67 def tearDown(self):64 def tearDown(self):
68 super(TestCameraDiskSpace, self).tearDown()65 super(TestCameraDiskSpace, self).tearDown()
69 os.remove(self.diskFiller) if os.path.exists(self.diskFiller) else None66 os.remove(self.diskFiller) if os.path.exists(self.diskFiller) else None
@@ -75,8 +72,8 @@
75 exposure_button = self.main_window.get_exposure_button()72 exposure_button = self.main_window.get_exposure_button()
76 no_space_hint = self.main_window.get_no_space_hint()73 no_space_hint = self.main_window.get_no_space_hint()
7774
78 self.assertThat(exposure_button.enabled, Equals(True))75 self.assertThat(exposure_button.enabled, Eventually(Equals(True)))
79 self.assertThat(no_space_hint.visible, Equals(False))76 self.assertThat(no_space_hint.visible, Eventually(Equals(False)))
8077
81 self.setFreeSpaceTo(CRITICAL_THRESHOLD - MEGABYTE)78 self.setFreeSpaceTo(CRITICAL_THRESHOLD - MEGABYTE)
82 self.assertThat(79 self.assertThat(
@@ -89,8 +86,8 @@
89 self.assertThat(86 self.assertThat(
90 self.diskSpaceAvailable(), GreaterThan(CRITICAL_THRESHOLD))87 self.diskSpaceAvailable(), GreaterThan(CRITICAL_THRESHOLD))
9188
92 self.assertThat(exposure_button.enabled, Equals(True))89 self.assertThat(exposure_button.enabled, Eventually(Equals(True)))
93 self.assertThat(no_space_hint.visible, Equals(False))90 self.assertThat(no_space_hint.visible, Eventually(Equals(False)))
9491
95 def test_low_disk(self):92 def test_low_disk(self):
96 """Verify proper behavior when disk space becomes low"""93 """Verify proper behavior when disk space becomes low"""
9794
=== modified file 'tests/autopilot/camera_app/tests/test_flash.py'
--- tests/autopilot/camera_app/tests/test_flash.py 2015-07-03 13:27:48 +0000
+++ tests/autopilot/camera_app/tests/test_flash.py 2016-03-23 15:42:41 +0000
@@ -16,21 +16,14 @@
16class TestCameraFlash(CameraAppTestCase):16class TestCameraFlash(CameraAppTestCase):
17 """Tests the flash"""17 """Tests the flash"""
1818
19 """ This is needed to wait for the application to start.
20 In the testfarm, the application may take some time to show up."""
21 def setUp(self):
22 super(TestCameraFlash, self).setUp()
23 self.assertThat(
24 self.main_window.get_qml_view().visible, Eventually(Equals(True)))
25
26 def tearDown(self):
27 super(TestCameraFlash, self).tearDown()
28
29 """Test that flash modes activate properly"""19 """Test that flash modes activate properly"""
30 def test_cycle_flash(self):20 def test_cycle_flash(self):
31 bottom_edge = self.main_window.get_bottom_edge()21 bottom_edge = self.main_window.get_bottom_edge()
32 bottom_edge.open()22 bottom_edge.open()
33 flash_button = self.main_window.get_flash_button()23 flash_button = self.main_window.get_flash_button()
24 if not flash_button:
25 return
26
34 option_value_selector = self.main_window.get_option_value_selector()27 option_value_selector = self.main_window.get_option_value_selector()
3528
36 # open option value selector showing the possible values29 # open option value selector showing the possible values
@@ -67,10 +60,13 @@
67 bottom_edge = self.main_window.get_bottom_edge()60 bottom_edge = self.main_window.get_bottom_edge()
68 bottom_edge.open()61 bottom_edge.open()
69 flash_button = self.main_window.get_video_flash_button()62 flash_button = self.main_window.get_video_flash_button()
63 if not flash_button:
64 return
65
70 option_value_selector = self.main_window.get_option_value_selector()66 option_value_selector = self.main_window.get_option_value_selector()
7167
72 # ensure initial state68 # ensure initial state
73 self.assertThat(flash_button.iconName, Equals("torch-off"))69 self.assertThat(flash_button.iconName, Eventually(Equals("torch-off")))
7470
75 # open option value selector showing the possible values71 # open option value selector showing the possible values
76 self.pointing_device.move_to_object(flash_button)72 self.pointing_device.move_to_object(flash_button)
@@ -83,19 +79,22 @@
83 option = self.main_window.get_option_value_button("On")79 option = self.main_window.get_option_value_button("On")
84 self.pointing_device.move_to_object(option)80 self.pointing_device.move_to_object(option)
85 self.pointing_device.click()81 self.pointing_device.click()
86 self.assertThat(flash_button.iconName, Equals("torch-on"))82 self.assertThat(flash_button.iconName, Eventually(Equals("torch-on")))
8783
88 # set flash to "off"84 # set flash to "off"
89 option = self.main_window.get_option_value_button("Off")85 option = self.main_window.get_option_value_button("Off")
90 self.pointing_device.move_to_object(option)86 self.pointing_device.move_to_object(option)
91 self.pointing_device.click()87 self.pointing_device.click()
92 self.assertThat(flash_button.iconName, Equals("torch-off"))88 self.assertThat(flash_button.iconName, Eventually(Equals("torch-off")))
9389
94 """Test that flash and hdr modes are mutually exclusive"""90 """Test that flash and hdr modes are mutually exclusive"""
95 def test_flash_hdr_mutually_exclusive(self):91 def test_flash_hdr_mutually_exclusive(self):
96 bottom_edge = self.main_window.get_bottom_edge()92 bottom_edge = self.main_window.get_bottom_edge()
97 bottom_edge.open()93 bottom_edge.open()
98 flash_button = self.main_window.get_flash_button()94 flash_button = self.main_window.get_flash_button()
95 if not flash_button:
96 return
97
99 hdr_button = self.main_window.get_hdr_button()98 hdr_button = self.main_window.get_hdr_button()
100 option_value_selector = self.main_window.get_option_value_selector()99 option_value_selector = self.main_window.get_option_value_selector()
101100
102101
=== modified file 'tests/autopilot/camera_app/tests/test_focus.py'
--- tests/autopilot/camera_app/tests/test_focus.py 2016-02-26 08:52:37 +0000
+++ tests/autopilot/camera_app/tests/test_focus.py 2016-03-23 15:42:41 +0000
@@ -18,26 +18,19 @@
18class TestFocus(CameraAppTestCase):18class TestFocus(CameraAppTestCase):
19 """Tests the focus"""19 """Tests the focus"""
2020
21 """ This is needed to wait for the application to start.
22 In the testfarm, the application may take some time to show up."""
23 def setUp(self):
24 super(TestFocus, self).setUp()
25 self.assertThat(
26 self.main_window.get_qml_view().visible, Eventually(Equals(True)))
27
28 def tearDown(self):
29 super(TestFocus, self).tearDown()
30
31 def verify_focus_ring_after_click_at(self, ring, x, y):21 def verify_focus_ring_after_click_at(self, ring, x, y):
32 # The focus ring should be invisible in the beginning22 # The focus ring should be invisible in the beginning
33 self.assertThat(ring.opacity, Eventually(Equals(0.0)))23 self.assertThat(ring.opacity, Eventually(Equals(0.0)))
3424
25 focus_mouse_area = self.main_window.get_focus_mouse_area()
26 self.assertThat(focus_mouse_area.enabled, Eventually(Equals(True)))
27
35 # Click in the designated spot28 # Click in the designated spot
36 self.pointing_device.move(x, y)29 self.pointing_device.move(x, y)
37 self.pointing_device.click()30 self.pointing_device.click()
3831
39 # The focus ring sould be visible now32 # The focus ring sould be visible now
40 self.assertThat(ring.opacity, Eventually(GreaterThan(0.5)))33 self.assertThat(ring.opacity, Eventually(GreaterThan(0.1)))
4134
42 # After some seconds the focus ring should fade out35 # After some seconds the focus ring should fade out
43 self.assertThat(ring.opacity, Eventually(Equals(0.0)))36 self.assertThat(ring.opacity, Eventually(Equals(0.0)))
@@ -45,19 +38,22 @@
45 """Test focusing in an area where we know the picture is"""38 """Test focusing in an area where we know the picture is"""
46 @unittest.skipIf(model() == 'Galaxy Nexus', 'Unusable with Mir on maguro')39 @unittest.skipIf(model() == 'Galaxy Nexus', 'Unusable with Mir on maguro')
47 def test_focus_valid_and_disappear(self):40 def test_focus_valid_and_disappear(self):
41 geometry = self.main_window.get_viewfinder_geometry()
48 focus_ring = self.main_window.get_focus_ring()42 focus_ring = self.main_window.get_focus_ring()
49 feed = self.main_window.get_viewfinder_geometry()
50 switch_cameras = self.main_window.get_swap_camera_button()43 switch_cameras = self.main_window.get_swap_camera_button()
51 exposure_button = self.main_window.get_exposure_button()44 exposure_button = self.main_window.get_exposure_button()
5245
53 # Click in the center of the viewfinder area46 # Click in the center of the viewfinder area
54 mid_x, mid_y = self.get_center(feed)47 mid_x, mid_y = self.get_center(geometry)
55 self.verify_focus_ring_after_click_at(focus_ring, mid_x, mid_y)48 self.verify_focus_ring_after_click_at(focus_ring, mid_x, mid_y)
5649
57 # Then try on the side edges and top edge to verify they50 # Then try on the side edges and top edge to verify they
58 # are focusable too51 # are focusable too
59 self.verify_focus_ring_after_click_at(focus_ring, 1, mid_y)52 self.verify_focus_ring_after_click_at(focus_ring,
60 self.verify_focus_ring_after_click_at(focus_ring, feed.width - 1,53 geometry.globalRect.x + 1, mid_y)
54 self.verify_focus_ring_after_click_at(focus_ring,
55 geometry.globalRect.x +
56 geometry.globalRect.width - 1,
61 mid_y)57 mid_y)
62 self.verify_focus_ring_after_click_at(focus_ring, mid_x, 1)58 self.verify_focus_ring_after_click_at(focus_ring, mid_x, 1)
6359
@@ -69,10 +65,13 @@
69 # Click in the center of the viewfinder area65 # Click in the center of the viewfinder area
70 self.verify_focus_ring_after_click_at(focus_ring, mid_x, mid_y)66 self.verify_focus_ring_after_click_at(focus_ring, mid_x, mid_y)
7167
72 # Then try on the side edges and top edge to verify they68 # Then try on the left, right and above the center to verify they
73 # are focusable too69 # are focusable too
74 self.verify_focus_ring_after_click_at(focus_ring, 1, mid_y)70 self.verify_focus_ring_after_click_at(focus_ring,
75 self.verify_focus_ring_after_click_at(focus_ring, feed.width - 1,71 geometry.globalRect.x + 1, mid_y)
72 self.verify_focus_ring_after_click_at(focus_ring,
73 geometry.globalRect.x +
74 geometry.globalRect.width - 1,
76 mid_y)75 mid_y)
77 self.verify_focus_ring_after_click_at(focus_ring, mid_x, 1)76 self.verify_focus_ring_after_click_at(focus_ring, mid_x, 1)
7877
7978
=== modified file 'tests/autopilot/camera_app/tests/test_gallery_view.py'
--- tests/autopilot/camera_app/tests/test_gallery_view.py 2015-04-29 15:56:44 +0000
+++ tests/autopilot/camera_app/tests/test_gallery_view.py 2016-03-23 15:42:41 +0000
@@ -7,7 +7,7 @@
77
8"""Tests for the Camera App zoom"""8"""Tests for the Camera App zoom"""
99
10from testtools.matchers import Equals10from testtools.matchers import Equals, NotEquals
11from autopilot.matchers import Eventually11from autopilot.matchers import Eventually
1212
13from camera_app.tests import CameraAppTestCase13from camera_app.tests import CameraAppTestCase
@@ -35,16 +35,19 @@
35 self.assertThat(slideshow_view.visible, Eventually(Equals(False)))35 self.assertThat(slideshow_view.visible, Eventually(Equals(False)))
36 self.assertThat(photogrid_view.visible, Eventually(Equals(True)))36 self.assertThat(photogrid_view.visible, Eventually(Equals(True)))
3737
38 def select_first_photo(self):38 def select_media(self, index=0):
39 # select the first photo39 media = self.main_window.get_media(index)
40 gallery = self.main_window.get_gallery()40 checkbox = media.wait_select_single(objectName="mediaItemCheckBox")
41 photo = gallery.wait_select_single(objectName="mediaItem0")41
42 self.pointing_device.move_to_object(photo)42 self.pointing_device.move_to_object(checkbox)
4343
44 # do a long press to enter Multiselection mode44 if checkbox.visible:
45 self.pointing_device.press()45 self.click()
46 sleep(1)46 else:
47 self.pointing_device.release()47 # do a long press to enter Multiselection mode
48 self.pointing_device.press()
49 sleep(1)
50 self.pointing_device.release()
4851
4952
50class TestCameraGalleryView(CameraAppTestCase, TestCameraGalleryViewMixin):53class TestCameraGalleryView(CameraAppTestCase, TestCameraGalleryViewMixin):
@@ -53,11 +56,6 @@
53 def setUp(self):56 def setUp(self):
54 self.delete_all_media()57 self.delete_all_media()
55 super(TestCameraGalleryView, self).setUp()58 super(TestCameraGalleryView, self).setUp()
56 self.assertThat(
57 self.main_window.get_qml_view().visible, Eventually(Equals(True)))
58
59 def tearDown(self):
60 super(TestCameraGalleryView, self).tearDown()
6159
62 """Tests swiping to the gallery and pressing the back button"""60 """Tests swiping to the gallery and pressing the back button"""
63 def test_swipe_to_gallery(self):61 def test_swipe_to_gallery(self):
@@ -112,26 +110,55 @@
112 def setUp(self):110 def setUp(self):
113 self.delete_all_media()111 self.delete_all_media()
114 self.add_sample_video()112 self.add_sample_video()
115
116 super(TestCameraGalleryViewWithVideo, self).setUp()113 super(TestCameraGalleryViewWithVideo, self).setUp()
117 self.assertThat(
118 self.main_window.get_qml_view().visible, Eventually(Equals(True)))
119
120 def tearDown(self):
121 super(TestCameraGalleryViewWithVideo, self).tearDown()
122114
123 """Tests the thumnails for video load correctly in slideshow view"""115 """Tests the thumnails for video load correctly in slideshow view"""
124 def test_video_thumbnails(self):116 def test_video_thumbnails(self):
125 viewfinder = self.main_window.get_viewfinder()117 viewfinder = self.main_window.get_viewfinder()
126 gallery = self.main_window.get_gallery()118 gallery = self.main_window.get_gallery()
127119 self.main_window.swipe_to_gallery(self)
128 self.main_window.swipe_to_gallery(self)120
129121 self.assertThat(viewfinder.inView, Eventually(Equals(False)))
130 self.assertThat(viewfinder.inView, Eventually(Equals(False)))122 self.assertThat(gallery.inView, Eventually(Equals(True)))
131 self.assertThat(gallery.inView, Eventually(Equals(True)))123
132124 spinner = gallery.wait_select_single("ActivityIndicator")
133 spinner = gallery.wait_select_single("ActivityIndicator")125 self.assertThat(spinner.running, Eventually(Equals(False)))
134 self.assertThat(spinner.running, Eventually(Equals(False)))126
127 thumb_error = self.main_window.get_broken_media_icon()
128 self.assertThat(thumb_error.opacity, Eventually(Equals(0.0)))
129
130 self.move_from_slideshow_to_photogrid()
131 thumb_error = self.main_window.get_broken_media_icon()
132 self.assertThat(thumb_error.opacity, Eventually(Equals(0.0)))
133
134
135class TestCameraGalleryViewWithBrokenVideo(
136 TestCameraGalleryViewMixin, CameraAppTestCase):
137 """Tests the camera gallery view with a broken video already present"""
138
139 def setUp(self):
140 self.delete_all_media()
141 self.add_sample_video(broken=True)
142 super(TestCameraGalleryViewWithBrokenVideo, self).setUp()
143
144 """Tests the placeholder thumnails for broken video loads correctly"""
145 def test_video_thumbnails(self):
146 viewfinder = self.main_window.get_viewfinder()
147 gallery = self.main_window.get_gallery()
148
149 self.main_window.swipe_to_gallery(self)
150 self.assertThat(viewfinder.inView, Eventually(Equals(False)))
151 self.assertThat(gallery.inView, Eventually(Equals(True)))
152
153 spinner = gallery.wait_select_single("ActivityIndicator")
154 self.assertThat(spinner.running, Eventually(Equals(False)))
155
156 thumb_error = self.main_window.get_broken_media_icon()
157 self.assertThat(thumb_error.opacity, Eventually(NotEquals(0.0)))
158
159 self.move_from_slideshow_to_photogrid()
160 thumb_error = self.main_window.get_broken_media_icon()
161 self.assertThat(thumb_error.opacity, Eventually(NotEquals(0.0)))
135162
136163
137class TestCameraGalleryViewWithPhoto(164class TestCameraGalleryViewWithPhoto(
@@ -141,25 +168,17 @@
141 def setUp(self):168 def setUp(self):
142 self.delete_all_media()169 self.delete_all_media()
143 self.add_sample_photo()170 self.add_sample_photo()
144
145 super(TestCameraGalleryViewWithPhoto, self).setUp()171 super(TestCameraGalleryViewWithPhoto, self).setUp()
146 self.assertThat(
147 self.main_window.get_qml_view().visible, Eventually(Equals(True)))
148
149 def tearDown(self):
150 super(TestCameraGalleryViewWithPhoto, self).tearDown()
151172
152 """Test deleting photo from multiselection"""173 """Test deleting photo from multiselection"""
153 def test_delete_photo_from_multiselection(self):174 def test_delete_photo_from_multiselection(self):
154 self.main_window.swipe_to_gallery(self)175 self.main_window.swipe_to_gallery(self)
155 self.move_from_slideshow_to_photogrid()176 self.move_from_slideshow_to_photogrid()
156 self.select_first_photo()177 self.select_media()
157178
158 # open actions drawer179 # open actions drawer
159 gallery = self.main_window.get_gallery()180 gallery = self.main_window.get_gallery()
160 opt = gallery.wait_select_single(objectName="additionalActionsButton")181 self.main_window.open_actions_drawer(gallery)
161 self.pointing_device.move_to_object(opt)
162 self.pointing_device.click()
163182
164 # click delete action button183 # click delete action button
165 delete = gallery.wait_select_single(objectName="actionButtonDelete")184 delete = gallery.wait_select_single(objectName="actionButtonDelete")
@@ -178,7 +197,7 @@
178 def test_multiselection_mode(self):197 def test_multiselection_mode(self):
179 self.main_window.swipe_to_gallery(self)198 self.main_window.swipe_to_gallery(self)
180 self.move_from_slideshow_to_photogrid()199 self.move_from_slideshow_to_photogrid()
181 self.select_first_photo()200 self.select_media()
182201
183 # exit the multiselection mode202 # exit the multiselection mode
184 gallery = self.main_window.get_gallery()203 gallery = self.main_window.get_gallery()
@@ -191,3 +210,61 @@
191210
192 self.assertThat(slideshow_view.visible, Eventually(Equals(False)))211 self.assertThat(slideshow_view.visible, Eventually(Equals(False)))
193 self.assertThat(photogrid_view.visible, Eventually(Equals(True)))212 self.assertThat(photogrid_view.visible, Eventually(Equals(True)))
213
214
215class TestCameraGalleryViewWithPhotosAndVideo(
216 TestCameraGalleryViewMixin, CameraAppTestCase):
217 """Tests the camera gallery view with two photos and a video"""
218
219 def setUp(self):
220 self.delete_all_media()
221 self.add_sample_photo()
222 self.add_sample_video()
223 super(TestCameraGalleryViewWithPhotosAndVideo, self).setUp()
224
225 def verify_share_state(self, expectedState, close=True):
226 gallery = self.main_window.get_gallery()
227 self.main_window.open_actions_drawer(gallery)
228
229 # verify expected state
230 share = gallery.wait_select_single(objectName="actionButtonShare")
231 self.assertThat(share.enabled, Eventually(Equals(expectedState)))
232
233 if (close):
234 self.main_window.close_actions_drawer(gallery)
235 else:
236 return share
237
238 """Tests share button enable or disabled correctly in multiselection"""
239 def test_multiselection_share_enabled(self):
240 self.main_window.swipe_to_gallery(self)
241 self.move_from_slideshow_to_photogrid()
242
243 # Verify options button disabled until we select something
244 gallery = self.main_window.get_gallery()
245 opt = gallery.wait_select_single(objectName="additionalActionsButton")
246 self.assertThat(opt.visible, Eventually(Equals(False)))
247
248 # Verify that if we select one photo options and share are enabled
249 self.select_media(0)
250 self.assertThat(opt.visible, Eventually(Equals(True)))
251 self.verify_share_state(True)
252
253 # Verify that it stays enabled with mixed media selected
254 self.select_media(1)
255 self.verify_share_state(True)
256
257 """Tests sharing with mixed media generates a warning dialog"""
258 def test_no_share_mixed_media(self):
259 self.main_window.swipe_to_gallery(self)
260 self.move_from_slideshow_to_photogrid()
261
262 self.select_media(0)
263 self.select_media(1)
264 share = self.verify_share_state(True, close=False)
265
266 self.pointing_device.move_to_object(share)
267 self.pointing_device.click()
268
269 gallery = self.main_window.get_gallery()
270 gallery.wait_select_single(objectName="unableShareDialog")
194271
=== modified file 'tests/autopilot/camera_app/tests/test_options.py'
--- tests/autopilot/camera_app/tests/test_options.py 2016-02-25 13:42:01 +0000
+++ tests/autopilot/camera_app/tests/test_options.py 2016-03-23 15:42:41 +0000
@@ -16,14 +16,6 @@
16class TestCameraOptions(CameraAppTestCase):16class TestCameraOptions(CameraAppTestCase):
17 """Tests the options overlay"""17 """Tests the options overlay"""
1818
19 """ This is needed to wait for the application to start.
20 In the testfarm, the application may take some time to show up."""
21 def setUp(self):
22 super(TestCameraOptions, self).setUp()
23 # FIXME: this should be in parent class
24 self.assertThat(
25 self.main_window.get_qml_view().visible, Eventually(Equals(True)))
26
27 """Test that the options overlay closes properly by tapping"""19 """Test that the options overlay closes properly by tapping"""
28 def test_overlay_tap_to_close(self):20 def test_overlay_tap_to_close(self):
29 bottom_edge = self.main_window.get_bottom_edge()21 bottom_edge = self.main_window.get_bottom_edge()
3022
=== modified file 'tests/autopilot/camera_app/tests/test_photo_editor.py'
--- tests/autopilot/camera_app/tests/test_photo_editor.py 2015-07-07 11:09:05 +0000
+++ tests/autopilot/camera_app/tests/test_photo_editor.py 2016-03-23 15:42:41 +0000
@@ -20,13 +20,7 @@
20 def setUp(self):20 def setUp(self):
21 self.delete_all_media()21 self.delete_all_media()
22 self.add_sample_photo()22 self.add_sample_photo()
23
24 super(TestCameraPhotoEditorWithPhoto, self).setUp()23 super(TestCameraPhotoEditorWithPhoto, self).setUp()
25 self.assertThat(
26 self.main_window.get_qml_view().visible, Eventually(Equals(True)))
27
28 def tearDown(self):
29 super(TestCameraPhotoEditorWithPhoto, self).tearDown()
3024
31 """Tests editor opening and closing correctly for pictures"""25 """Tests editor opening and closing correctly for pictures"""
32 def test_editor_appears(self):26 def test_editor_appears(self):
@@ -37,11 +31,7 @@
37 self.main_window.swipe_to_gallery(self)31 self.main_window.swipe_to_gallery(self)
3832
39 self.assertThat(gallery.inView, Eventually(Equals(True)))33 self.assertThat(gallery.inView, Eventually(Equals(True)))
4034 self.main_window.open_actions_drawer(gallery)
41 # open actions drawer
42 opt = gallery.wait_select_single(objectName="additionalActionsButton")
43 self.pointing_device.move_to_object(opt)
44 self.pointing_device.click()
4535
46 # If the editor button is not there when in the gallery view, then36 # If the editor button is not there when in the gallery view, then
47 # we are not on a system that has the UI extras package installed or37 # we are not on a system that has the UI extras package installed or
@@ -77,13 +67,7 @@
77 def setUp(self):67 def setUp(self):
78 self.delete_all_media()68 self.delete_all_media()
79 self.add_sample_video()69 self.add_sample_video()
80
81 super(TestCameraPhotoEditorWithVideo, self).setUp()70 super(TestCameraPhotoEditorWithVideo, self).setUp()
82 self.assertThat(
83 self.main_window.get_qml_view().visible, Eventually(Equals(True)))
84
85 def tearDown(self):
86 super(TestCameraPhotoEditorWithVideo, self).tearDown()
8771
88 """Tests editor not being available for videos"""72 """Tests editor not being available for videos"""
89 def test_editor_not_on_videos(self):73 def test_editor_not_on_videos(self):
@@ -95,11 +79,7 @@
95 self.main_window.swipe_to_gallery(self)79 self.main_window.swipe_to_gallery(self)
9680
97 self.assertThat(gallery.inView, Eventually(Equals(True)))81 self.assertThat(gallery.inView, Eventually(Equals(True)))
9882 self.main_window.open_actions_drawer(gallery)
99 # open actions drawer
100 opt = gallery.wait_select_single(objectName="additionalActionsButton")
101 self.pointing_device.move_to_object(opt)
102 self.pointing_device.click()
10383
104 # If the editor button is not there when in the gallery view, then84 # If the editor button is not there when in the gallery view, then
105 # we are not on a system that has the UI extras package installed or85 # we are not on a system that has the UI extras package installed or
10686
=== modified file 'tests/autopilot/camera_app/tests/test_zoom.py'
--- tests/autopilot/camera_app/tests/test_zoom.py 2016-01-11 17:07:21 +0000
+++ tests/autopilot/camera_app/tests/test_zoom.py 2016-03-23 15:42:41 +0000
@@ -19,16 +19,6 @@
19class TestCameraZoom(CameraAppTestCase):19class TestCameraZoom(CameraAppTestCase):
20 """Tests the main camera features"""20 """Tests the main camera features"""
2121
22 """ This is needed to wait for the application to start.
23 In the testfarm, the application may take some time to show up."""
24 def setUp(self):
25 super(TestCameraZoom, self).setUp()
26 self.assertThat(
27 self.main_window.get_qml_view().visible, Eventually(Equals(True)))
28
29 def tearDown(self):
30 super(TestCameraZoom, self).tearDown()
31
32 def activate_zoom(self):22 def activate_zoom(self):
33 viewfinder = self.main_window.get_viewfinder_geometry()23 viewfinder = self.main_window.get_viewfinder_geometry()
34 viewfinder_center = self.get_center(viewfinder)24 viewfinder_center = self.get_center(viewfinder)
3525
=== modified file 'tests/unittests/CMakeLists.txt'
--- tests/unittests/CMakeLists.txt 2016-02-26 15:24:03 +0000
+++ tests/unittests/CMakeLists.txt 2016-03-23 15:42:41 +0000
@@ -7,7 +7,7 @@
7add_executable(tst_QmlTests tst_QmlTests.cpp)7add_executable(tst_QmlTests tst_QmlTests.cpp)
8qt5_use_modules(tst_QmlTests Core Qml Quick Test QuickTest)8qt5_use_modules(tst_QmlTests Core Qml Quick Test QuickTest)
9target_link_libraries(tst_QmlTests ${TPL_QT5_LIBRARIES})9target_link_libraries(tst_QmlTests ${TPL_QT5_LIBRARIES})
10add_test(tst_QmlTests ${XVFB_RUN_CMD} ${CMAKE_CURRENT_BINARY_DIR}/tst_QmlTests -import ${CMAKE_SOURCE_DIR})10add_test(tst_QmlTests ${XVFB_RUN_CMD} ${CMAKE_CURRENT_BINARY_DIR}/tst_QmlTests -import ${CMAKE_SOURCE_DIR} -import ${CMAKE_BINARY_DIR})
1111
12# copy qml test files to build dir12# copy qml test files to build dir
13file(GLOB qmlTestFiles RELATIVE ${CMAKE_SOURCE_DIR}/tests/unittests/ *qml)13file(GLOB qmlTestFiles RELATIVE ${CMAKE_SOURCE_DIR}/tests/unittests/ *qml)
1414
=== added file 'tests/unittests/tst_PhotogridView.qml'
--- tests/unittests/tst_PhotogridView.qml 1970-01-01 00:00:00 +0000
+++ tests/unittests/tst_PhotogridView.qml 2016-03-23 15:42:41 +0000
@@ -0,0 +1,88 @@
1/*
2 * Copyright 2016 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18import QtQuick 2.4
19import QtTest 1.0
20import "../../"
21import "../../.." //Needed for out of source build
22
23TestCase {
24 name: "PhotogridView"
25
26 function test_mixedMediaSelection_data() {
27 return [
28 { // one item only
29 isMixedMedia: false,
30 listItems: [
31 { fileType: "image", selected: true, fileURL: "" }
32 ]
33 },
34 { // mixed media but only non-mixed selected
35 isMixedMedia: false,
36 listItems: [
37 { fileType: "video", selected: false, fileURL: "" },
38 { fileType: "image", selected: true, fileURL: "" },
39 { fileType: "image", selected: true, fileURL: "" }
40 ]
41 },
42 { // mixed media
43 isMixedMedia: true,
44 listItems: [
45 { fileType: "video", selected: true, fileURL: "" },
46 { fileType: "image", selected: true, fileURL: "" },
47 { fileType: "image", selected: true, fileURL: "" }
48 ]
49 },
50 ];
51 }
52
53 function test_mixedMediaSelection(data) {
54 list.clear()
55 list.data = data.listItems;
56 for (var i = 0; i < data.listItems.length; i++) {
57 list.append(data.listItems[i]);
58 }
59 list.updateSelectedFiles();
60 grid.model = list
61 compare(grid.selectionContainsMixedMedia(), data.isMixedMedia, "Mixed media not detected correctly")
62 }
63
64 ListModel {
65 id: list
66 property var data
67 property var selectedFiles: []
68 function updateSelectedFiles() {
69 // need to re-assign entire list due to the way list properties work in QML
70 var selected = [];
71 for (var i = 0; i < list.count; i++) {
72 if (list.data[i].selected) selected.push(i);
73 }
74 list.selectedFiles = selected;
75 }
76 function get(i, key) {
77 return list.data[i][key];
78 }
79 }
80
81 PhotogridView {
82 id: grid
83 width: 600
84 height: 800
85 inView: true
86 inSelectionMode: true
87 }
88}

Subscribers

People subscribed via source and target branches