Merge lp:camera-app/staging into lp:camera-app
- staging
- Merge into trunk
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 | ||||||||||||||||||||||||||||||||
Related bugs: |
|
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.
- 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.
Description of the change
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
1 | === modified file 'GalleryView.qml' | |||
2 | --- GalleryView.qml 2016-02-23 11:46:52 +0000 | |||
3 | +++ GalleryView.qml 2016-03-23 15:42:41 +0000 | |||
4 | @@ -16,7 +16,7 @@ | |||
5 | 16 | 16 | ||
6 | 17 | import QtQuick 2.4 | 17 | import QtQuick 2.4 |
7 | 18 | import Ubuntu.Components 1.3 | 18 | import Ubuntu.Components 1.3 |
9 | 19 | import Ubuntu.Content 0.1 | 19 | import Ubuntu.Content 1.3 |
10 | 20 | import Ubuntu.Thumbnailer 0.1 | 20 | import Ubuntu.Thumbnailer 0.1 |
11 | 21 | import CameraApp 0.1 | 21 | import CameraApp 0.1 |
12 | 22 | import "MimeTypeMapper.js" as MimeTypeMapper | 22 | import "MimeTypeMapper.js" as MimeTypeMapper |
13 | @@ -35,7 +35,7 @@ | |||
14 | 35 | StorageLocations.removableStorageVideosLocation] | 35 | StorageLocations.removableStorageVideosLocation] |
15 | 36 | typeFilters: !main.contentExportMode ? [ "image", "video" ] | 36 | typeFilters: !main.contentExportMode ? [ "image", "video" ] |
16 | 37 | : [MimeTypeMapper.contentTypeToMimeType(main.transferContentType)] | 37 | : [MimeTypeMapper.contentTypeToMimeType(main.transferContentType)] |
18 | 38 | singleSelectionOnly: main.transfer.selectionType === ContentTransfer.Single | 38 | singleSelectionOnly: main.contentExportMode && main.transfer.selectionType === ContentTransfer.Single |
19 | 39 | } | 39 | } |
20 | 40 | 40 | ||
21 | 41 | property bool gridMode: main.contentExportMode | 41 | property bool gridMode: main.contentExportMode |
22 | @@ -58,7 +58,6 @@ | |||
23 | 58 | 58 | ||
24 | 59 | function exitUserSelectionMode() { | 59 | function exitUserSelectionMode() { |
25 | 60 | model.clearSelection(); | 60 | model.clearSelection(); |
26 | 61 | model.singleSelectionOnly = true; | ||
27 | 62 | userSelectionMode = false; | 61 | userSelectionMode = false; |
28 | 63 | } | 62 | } |
29 | 64 | 63 | ||
30 | @@ -76,6 +75,7 @@ | |||
31 | 76 | model: galleryView.model | 75 | model: galleryView.model |
32 | 77 | visible: opacity != 0.0 | 76 | visible: opacity != 0.0 |
33 | 78 | inView: galleryView.inView && galleryView.currentView == slideshowView | 77 | inView: galleryView.inView && galleryView.currentView == slideshowView |
34 | 78 | focus: inView | ||
35 | 79 | inSelectionMode: main.contentExportMode || galleryView.userSelectionMode | 79 | inSelectionMode: main.contentExportMode || galleryView.userSelectionMode |
36 | 80 | onToggleSelection: model.toggleSelected(currentIndex) | 80 | onToggleSelection: model.toggleSelected(currentIndex) |
37 | 81 | onToggleHeader: header.toggle(); | 81 | onToggleHeader: header.toggle(); |
38 | @@ -89,6 +89,7 @@ | |||
39 | 89 | model: galleryView.model | 89 | model: galleryView.model |
40 | 90 | visible: opacity != 0.0 | 90 | visible: opacity != 0.0 |
41 | 91 | inView: galleryView.inView && galleryView.currentView == photogridView | 91 | inView: galleryView.inView && galleryView.currentView == photogridView |
42 | 92 | focus: inView | ||
43 | 92 | inSelectionMode: main.contentExportMode || galleryView.userSelectionMode | 93 | inSelectionMode: main.contentExportMode || galleryView.userSelectionMode |
44 | 93 | onPhotoClicked: { | 94 | onPhotoClicked: { |
45 | 94 | slideshowView.showPhotoAtIndex(index); | 95 | slideshowView.showPhotoAtIndex(index); |
46 | @@ -97,7 +98,6 @@ | |||
47 | 97 | onPhotoPressAndHold: { | 98 | onPhotoPressAndHold: { |
48 | 98 | if (!galleryView.userSelectionMode) { | 99 | if (!galleryView.userSelectionMode) { |
49 | 99 | galleryView.userSelectionMode = true; | 100 | galleryView.userSelectionMode = true; |
50 | 100 | model.singleSelectionOnly = false; | ||
51 | 101 | model.toggleSelected(index); | 101 | model.toggleSelected(index); |
52 | 102 | } | 102 | } |
53 | 103 | } | 103 | } |
54 | 104 | 104 | ||
55 | === modified file 'GalleryViewHeader.qml' | |||
56 | --- GalleryViewHeader.qml 2016-02-25 13:01:05 +0000 | |||
57 | +++ GalleryViewHeader.qml 2016-03-23 15:42:41 +0000 | |||
58 | @@ -153,6 +153,7 @@ | |||
59 | 153 | 153 | ||
60 | 154 | Item { | 154 | Item { |
61 | 155 | id: actionsDrawer | 155 | id: actionsDrawer |
62 | 156 | objectName: "actionsDrawer" | ||
63 | 156 | 157 | ||
64 | 157 | anchors { | 158 | anchors { |
65 | 158 | top: parent.bottom | 159 | top: parent.bottom |
66 | @@ -170,6 +171,8 @@ | |||
67 | 170 | } | 171 | } |
68 | 171 | 172 | ||
69 | 172 | property bool opened: false | 173 | property bool opened: false |
70 | 174 | property bool fullyOpened: actionsColumn.y == 0 | ||
71 | 175 | property bool fullyClosed: actionsColumn.y == -actionsColumn.height | ||
72 | 173 | property list<Action> actions | 176 | property list<Action> actions |
73 | 174 | 177 | ||
74 | 175 | onOpenedChanged: { | 178 | onOpenedChanged: { |
75 | 176 | 179 | ||
76 | === modified file 'MimeTypeMapper.js' | |||
77 | --- MimeTypeMapper.js 2014-07-31 18:41:17 +0000 | |||
78 | +++ MimeTypeMapper.js 2016-03-23 15:42:41 +0000 | |||
79 | @@ -17,7 +17,7 @@ | |||
80 | 17 | */ | 17 | */ |
81 | 18 | 18 | ||
82 | 19 | .pragma library | 19 | .pragma library |
84 | 20 | .import Ubuntu.Content 0.1 as UbuntuContent | 20 | .import Ubuntu.Content 1.3 as UbuntuContent |
85 | 21 | 21 | ||
86 | 22 | function startsWith(string, prefix) { | 22 | function startsWith(string, prefix) { |
87 | 23 | return string.indexOf(prefix) === 0; | 23 | return string.indexOf(prefix) === 0; |
88 | 24 | 24 | ||
89 | === modified file 'OptionButton.qml' | |||
90 | --- OptionButton.qml 2015-11-24 15:44:58 +0000 | |||
91 | +++ OptionButton.qml 2016-03-23 15:42:41 +0000 | |||
92 | @@ -26,8 +26,8 @@ | |||
93 | 26 | iconName: !model.get(model.selectedIndex).icon ? model.icon : model.get(model.selectedIndex).icon | 26 | iconName: !model.get(model.selectedIndex).icon ? model.icon : model.get(model.selectedIndex).icon |
94 | 27 | iconSource: (model && model.iconSource) ? model.iconSource : "" | 27 | iconSource: (model && model.iconSource) ? model.iconSource : "" |
95 | 28 | on: model.isToggle ? model.get(model.selectedIndex).value : true | 28 | on: model.isToggle ? model.get(model.selectedIndex).value : true |
97 | 29 | enabled: model.available | 29 | enabled: model.visible && model.available |
98 | 30 | label: model.label | 30 | label: model.label |
100 | 31 | visible: model.visible | 31 | visible: model.visible && model.available |
101 | 32 | automaticOrientation: false | 32 | automaticOrientation: false |
102 | 33 | } | 33 | } |
103 | 34 | 34 | ||
104 | === modified file 'PhotogridView.qml' | |||
105 | --- PhotogridView.qml 2016-02-23 11:46:52 +0000 | |||
106 | +++ PhotogridView.qml 2016-03-23 15:42:41 +0000 | |||
107 | @@ -18,11 +18,11 @@ | |||
108 | 18 | import Ubuntu.Components 1.3 | 18 | import Ubuntu.Components 1.3 |
109 | 19 | import Ubuntu.Components.Popups 1.3 | 19 | import Ubuntu.Components.Popups 1.3 |
110 | 20 | import Ubuntu.Thumbnailer 0.1 | 20 | import Ubuntu.Thumbnailer 0.1 |
112 | 21 | import Ubuntu.Content 0.1 | 21 | import Ubuntu.Content 1.3 |
113 | 22 | import CameraApp 0.1 | 22 | import CameraApp 0.1 |
114 | 23 | import "MimeTypeMapper.js" as MimeTypeMapper | 23 | import "MimeTypeMapper.js" as MimeTypeMapper |
115 | 24 | 24 | ||
117 | 25 | Item { | 25 | FocusScope { |
118 | 26 | id: photogridView | 26 | id: photogridView |
119 | 27 | 27 | ||
120 | 28 | property var model | 28 | property var model |
121 | @@ -40,11 +40,14 @@ | |||
122 | 40 | Action { | 40 | Action { |
123 | 41 | text: i18n.tr("Share") | 41 | text: i18n.tr("Share") |
124 | 42 | iconName: "share" | 42 | iconName: "share" |
126 | 43 | enabled: model.selectedFiles.length <= 1 | 43 | enabled: model.selectedFiles.length > 0 |
127 | 44 | onTriggered: { | 44 | onTriggered: { |
131 | 45 | if (model.selectedFiles.length > 0) { | 45 | // Display a warning message if we are attempting to share mixed |
132 | 46 | var dialog = PopupUtils.open(sharePopoverComponent) | 46 | // content, as the framework does not properly support this |
133 | 47 | dialog.parent = photogridView | 47 | if (selectionContainsMixedMedia()) { |
134 | 48 | PopupUtils.open(unableShareDialogComponent).parent = photogridView; | ||
135 | 49 | } else { | ||
136 | 50 | PopupUtils.open(sharePopoverComponent).parent = photogridView; | ||
137 | 48 | } | 51 | } |
138 | 49 | } | 52 | } |
139 | 50 | }, | 53 | }, |
140 | @@ -60,6 +63,19 @@ | |||
141 | 60 | } | 63 | } |
142 | 61 | ] | 64 | ] |
143 | 62 | 65 | ||
144 | 66 | function selectionContainsMixedMedia() { | ||
145 | 67 | var selection = model.selectedFiles; | ||
146 | 68 | var lastType = model.get(selection[0], "fileType"); | ||
147 | 69 | for (var i = 1; i < selection.length; i++) { | ||
148 | 70 | var type = model.get(selection[i], "fileType"); | ||
149 | 71 | if (type !== lastType) { | ||
150 | 72 | return true; | ||
151 | 73 | } | ||
152 | 74 | lastType = type; | ||
153 | 75 | } | ||
154 | 76 | return false; | ||
155 | 77 | } | ||
156 | 78 | |||
157 | 63 | function showPhotoAtIndex(index) { | 79 | function showPhotoAtIndex(index) { |
158 | 64 | gridView.positionViewAtIndex(index, GridView.Center); | 80 | gridView.positionViewAtIndex(index, GridView.Center); |
159 | 65 | } | 81 | } |
160 | @@ -139,6 +155,16 @@ | |||
161 | 139 | visible: isVideo | 155 | visible: isVideo |
162 | 140 | } | 156 | } |
163 | 141 | 157 | ||
164 | 158 | Icon { | ||
165 | 159 | objectName: "thumbnailLoadingErrorIcon" | ||
166 | 160 | anchors.centerIn: parent | ||
167 | 161 | width: units.gu(6) | ||
168 | 162 | height: width | ||
169 | 163 | name: cellDelegate.isVideo ? "stock_video" : "stock_image" | ||
170 | 164 | color: "white" | ||
171 | 165 | opacity: thumbnail.status == Image.Error ? 1.0 : 0.0 | ||
172 | 166 | } | ||
173 | 167 | |||
174 | 142 | MouseArea { | 168 | MouseArea { |
175 | 143 | anchors.fill: parent | 169 | anchors.fill: parent |
176 | 144 | onClicked: photogridView.photoClicked(index) | 170 | onClicked: photogridView.photoClicked(index) |
177 | @@ -160,6 +186,7 @@ | |||
178 | 160 | visible: inSelectionMode | 186 | visible: inSelectionMode |
179 | 161 | 187 | ||
180 | 162 | Icon { | 188 | Icon { |
181 | 189 | objectName: "mediaItemCheckBox" | ||
182 | 163 | anchors.centerIn: parent | 190 | anchors.centerIn: parent |
183 | 164 | width: parent.width * 0.8 | 191 | width: parent.width * 0.8 |
184 | 165 | height: parent.height * 0.8 | 192 | height: parent.height * 0.8 |
185 | @@ -225,4 +252,13 @@ | |||
186 | 225 | onVisibleChanged: photogridView.toggleHeader() | 252 | onVisibleChanged: photogridView.toggleHeader() |
187 | 226 | } | 253 | } |
188 | 227 | } | 254 | } |
189 | 255 | |||
190 | 256 | Component { | ||
191 | 257 | id: unableShareDialogComponent | ||
192 | 258 | UnableShareDialog { | ||
193 | 259 | objectName: "unableShareDialog" | ||
194 | 260 | onVisibleChanged: photogridView.toggleHeader() | ||
195 | 261 | } | ||
196 | 262 | } | ||
197 | 263 | |||
198 | 228 | } | 264 | } |
199 | 229 | 265 | ||
200 | === modified file 'SlideshowView.qml' | |||
201 | --- SlideshowView.qml 2016-02-23 11:46:52 +0000 | |||
202 | +++ SlideshowView.qml 2016-03-23 15:42:41 +0000 | |||
203 | @@ -18,12 +18,12 @@ | |||
204 | 18 | import Ubuntu.Components 1.3 | 18 | import Ubuntu.Components 1.3 |
205 | 19 | import Ubuntu.Components.ListItems 1.3 as ListItems | 19 | import Ubuntu.Components.ListItems 1.3 as ListItems |
206 | 20 | import Ubuntu.Components.Popups 1.3 | 20 | import Ubuntu.Components.Popups 1.3 |
208 | 21 | import Ubuntu.Content 0.1 | 21 | import Ubuntu.Content 1.3 |
209 | 22 | import Ubuntu.Thumbnailer 0.1 | 22 | import Ubuntu.Thumbnailer 0.1 |
210 | 23 | import CameraApp 0.1 | 23 | import CameraApp 0.1 |
211 | 24 | import "MimeTypeMapper.js" as MimeTypeMapper | 24 | import "MimeTypeMapper.js" as MimeTypeMapper |
212 | 25 | 25 | ||
214 | 26 | Item { | 26 | FocusScope { |
215 | 27 | id: slideshowView | 27 | id: slideshowView |
216 | 28 | 28 | ||
217 | 29 | property var model | 29 | property var model |
218 | @@ -111,14 +111,12 @@ | |||
219 | 111 | 111 | ||
220 | 112 | anchors.fill: parent | 112 | anchors.fill: parent |
221 | 113 | model: slideshowView.model | 113 | model: slideshowView.model |
222 | 114 | focus: true | ||
223 | 114 | orientation: ListView.Horizontal | 115 | orientation: ListView.Horizontal |
224 | 115 | boundsBehavior: Flickable.StopAtBounds | 116 | boundsBehavior: Flickable.StopAtBounds |
225 | 116 | cacheBuffer: width | 117 | cacheBuffer: width |
226 | 117 | highlightRangeMode: ListView.StrictlyEnforceRange | 118 | highlightRangeMode: ListView.StrictlyEnforceRange |
231 | 118 | // FIXME: this disables the animation introduced by highlightRangeMode | 119 | highlightMoveDuration: UbuntuAnimation.FastDuration |
228 | 119 | // happening setting currentIndex; it is necessary at least because we | ||
229 | 120 | // were hitting https://bugreports.qt-project.org/browse/QTBUG-41035 | ||
230 | 121 | highlightMoveDuration: 0 | ||
232 | 122 | snapMode: ListView.SnapOneItem | 120 | snapMode: ListView.SnapOneItem |
233 | 123 | onCountChanged: { | 121 | onCountChanged: { |
234 | 124 | // currentIndex is -1 by default and stays so until manually set to something else | 122 | // currentIndex is -1 by default and stays so until manually set to something else |
235 | @@ -140,6 +138,7 @@ | |||
236 | 140 | } | 138 | } |
237 | 141 | delegate: Item { | 139 | delegate: Item { |
238 | 142 | id: delegate | 140 | id: delegate |
239 | 141 | objectName: "mediaItem" + index | ||
240 | 143 | property bool pinchInProgress: zoomPinchArea.active | 142 | property bool pinchInProgress: zoomPinchArea.active |
241 | 144 | property string url: fileURL | 143 | property string url: fileURL |
242 | 145 | property bool isSelected: selected | 144 | property bool isSelected: selected |
243 | @@ -183,6 +182,7 @@ | |||
244 | 183 | property real maximumZoom: 3.0 | 182 | property real maximumZoom: 3.0 |
245 | 184 | property bool active: false | 183 | property bool active: false |
246 | 185 | property var center | 184 | property var center |
247 | 185 | enabled: !media.isVideo | ||
248 | 186 | 186 | ||
249 | 187 | onPinchStarted: { | 187 | onPinchStarted: { |
250 | 188 | active = true; | 188 | active = true; |
251 | @@ -258,6 +258,16 @@ | |||
252 | 258 | } | 258 | } |
253 | 259 | fillMode: Image.PreserveAspectFit | 259 | fillMode: Image.PreserveAspectFit |
254 | 260 | } | 260 | } |
255 | 261 | |||
256 | 262 | Icon { | ||
257 | 263 | objectName: "thumbnailLoadingErrorIcon" | ||
258 | 264 | anchors.centerIn: parent | ||
259 | 265 | width: units.gu(30) | ||
260 | 266 | height: width | ||
261 | 267 | name: media.isVideo ? "stock_video" : "stock_image" | ||
262 | 268 | color: "white" | ||
263 | 269 | opacity: image.status == Image.Error ? 1.0 : 0.0 | ||
264 | 270 | } | ||
265 | 261 | } | 271 | } |
266 | 262 | 272 | ||
267 | 263 | Icon { | 273 | Icon { |
268 | 264 | 274 | ||
269 | === added file 'UnableShareDialog.qml' | |||
270 | --- UnableShareDialog.qml 1970-01-01 00:00:00 +0000 | |||
271 | +++ UnableShareDialog.qml 2016-03-23 15:42:41 +0000 | |||
272 | @@ -0,0 +1,34 @@ | |||
273 | 1 | /* | ||
274 | 2 | * Copyright (C) 2016 Canonical Ltd | ||
275 | 3 | * | ||
276 | 4 | * This program is free software: you can redistribute it and/or modify | ||
277 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
278 | 6 | * published by the Free Software Foundation. | ||
279 | 7 | * | ||
280 | 8 | * This program is distributed in the hope that it will be useful, | ||
281 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
282 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
283 | 11 | * GNU General Public License for more details. | ||
284 | 12 | * | ||
285 | 13 | * You should have received a copy of the GNU General Public License | ||
286 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
287 | 15 | */ | ||
288 | 16 | |||
289 | 17 | import QtQuick 2.4 | ||
290 | 18 | import Ubuntu.Components 1.3 | ||
291 | 19 | import Ubuntu.Components.Popups 1.3 | ||
292 | 20 | |||
293 | 21 | Dialog { | ||
294 | 22 | id: dialog | ||
295 | 23 | objectName: "unableShareDialog" | ||
296 | 24 | |||
297 | 25 | title: i18n.tr("Unable to share") | ||
298 | 26 | text: i18n.tr("Unable to share photos and videos at the same time") | ||
299 | 27 | |||
300 | 28 | Button { | ||
301 | 29 | objectName: "unableShareDialogOk" | ||
302 | 30 | text: i18n.tr("Ok") | ||
303 | 31 | color: UbuntuColors.orange | ||
304 | 32 | onClicked: PopupUtils.close(dialog); | ||
305 | 33 | } | ||
306 | 34 | } | ||
307 | 0 | 35 | ||
308 | === modified file 'ViewFinderOverlay.qml' | |||
309 | --- ViewFinderOverlay.qml 2016-02-23 11:46:52 +0000 | |||
310 | +++ ViewFinderOverlay.qml 2016-03-23 15:42:41 +0000 | |||
311 | @@ -33,6 +33,7 @@ | |||
312 | 33 | property var controls: controls | 33 | property var controls: controls |
313 | 34 | property var settings: settings | 34 | property var settings: settings |
314 | 35 | property bool readyForCapture | 35 | property bool readyForCapture |
315 | 36 | property int sensorOrientation | ||
316 | 36 | 37 | ||
317 | 37 | function showFocusRing(x, y) { | 38 | function showFocusRing(x, y) { |
318 | 38 | focusRing.center = Qt.point(x, y); | 39 | focusRing.center = Qt.point(x, y); |
319 | @@ -289,6 +290,7 @@ | |||
320 | 289 | id: bottomEdgeClose | 290 | id: bottomEdgeClose |
321 | 290 | anchors.fill: parent | 291 | anchors.fill: parent |
322 | 291 | onClicked: optionsOverlayClose() | 292 | onClicked: optionsOverlayClose() |
323 | 293 | enabled: !camera.timedCaptureInProgress | ||
324 | 292 | } | 294 | } |
325 | 293 | 295 | ||
326 | 294 | OrientationHelper { | 296 | OrientationHelper { |
327 | @@ -305,8 +307,9 @@ | |||
328 | 305 | height: optionsOverlayLoader.height | 307 | height: optionsOverlayLoader.height |
329 | 306 | onOpenedChanged: optionsOverlayLoader.item.closeValueSelector() | 308 | onOpenedChanged: optionsOverlayLoader.item.closeValueSelector() |
330 | 307 | enabled: camera.videoRecorder.recorderState == CameraRecorder.StoppedState | 309 | enabled: camera.videoRecorder.recorderState == CameraRecorder.StoppedState |
332 | 308 | && !camera.photoCaptureInProgress | 310 | && !camera.photoCaptureInProgress && !camera.timedCaptureInProgress |
333 | 309 | opacity: enabled ? 1.0 : 0.3 | 311 | opacity: enabled ? 1.0 : 0.3 |
334 | 312 | property bool ready: optionsOverlayLoader.status == Loader.Ready | ||
335 | 310 | 313 | ||
336 | 311 | /* At startup, opened is false and 'bottomEdge.height' is 0 until | 314 | /* At startup, opened is false and 'bottomEdge.height' is 0 until |
337 | 312 | optionsOverlayLoader has finished loading. When that happens | 315 | optionsOverlayLoader has finished loading. When that happens |
338 | @@ -652,13 +655,15 @@ | |||
339 | 652 | enabled: visible | 655 | enabled: visible |
340 | 653 | 656 | ||
341 | 654 | function timedShoot(secs) { | 657 | function timedShoot(secs) { |
342 | 658 | camera.timedCaptureInProgress = true; | ||
343 | 655 | timedShootFeedback.start(); | 659 | timedShootFeedback.start(); |
344 | 656 | shootingTimer.remainingSecs = secs; | 660 | shootingTimer.remainingSecs = secs; |
345 | 657 | shootingTimer.start(); | 661 | shootingTimer.start(); |
346 | 658 | } | 662 | } |
347 | 659 | 663 | ||
348 | 660 | function cancelTimedShoot() { | 664 | function cancelTimedShoot() { |
350 | 661 | if (shootingTimer.running) { | 665 | if (camera.timedCaptureInProgress) { |
351 | 666 | camera.timedCaptureInProgress = false; | ||
352 | 662 | shootingTimer.stop(); | 667 | shootingTimer.stop(); |
353 | 663 | timedShootFeedback.stop(); | 668 | timedShootFeedback.stop(); |
354 | 664 | } | 669 | } |
355 | @@ -692,9 +697,8 @@ | |||
356 | 692 | break; | 697 | break; |
357 | 693 | } | 698 | } |
358 | 694 | 699 | ||
362 | 695 | if (Screen.primaryOrientation == Qt.PortraitOrientation) { | 700 | // account for the orientation of the sensor |
363 | 696 | orientation += 90; | 701 | orientation -= viewFinderOverlay.sensorOrientation; |
361 | 697 | } | ||
364 | 698 | 702 | ||
365 | 699 | if (camera.captureMode == Camera.CaptureVideo) { | 703 | if (camera.captureMode == Camera.CaptureVideo) { |
366 | 700 | if (main.contentExportMode) { | 704 | if (main.contentExportMode) { |
367 | @@ -774,6 +778,7 @@ | |||
368 | 774 | onTriggered: { | 778 | onTriggered: { |
369 | 775 | if (remainingSecs == 0) { | 779 | if (remainingSecs == 0) { |
370 | 776 | running = false; | 780 | running = false; |
371 | 781 | camera.timedCaptureInProgress = false; | ||
372 | 777 | controls.shoot(); | 782 | controls.shoot(); |
373 | 778 | timedShootFeedback.stop(); | 783 | timedShootFeedback.stop(); |
374 | 779 | } else { | 784 | } else { |
375 | @@ -819,7 +824,7 @@ | |||
376 | 819 | iconName: (camera.captureMode == Camera.CaptureStillImage) ? "camcorder" : "camera-symbolic" | 824 | iconName: (camera.captureMode == Camera.CaptureStillImage) ? "camcorder" : "camera-symbolic" |
377 | 820 | onClicked: controls.changeRecordMode() | 825 | onClicked: controls.changeRecordMode() |
378 | 821 | enabled: camera.videoRecorder.recorderState == CameraRecorder.StoppedState && !main.contentExportMode | 826 | enabled: camera.videoRecorder.recorderState == CameraRecorder.StoppedState && !main.contentExportMode |
380 | 822 | && !camera.photoCaptureInProgress | 827 | && !camera.photoCaptureInProgress && !camera.timedCaptureInProgress |
381 | 823 | } | 828 | } |
382 | 824 | 829 | ||
383 | 825 | ShootButton { | 830 | ShootButton { |
384 | @@ -833,6 +838,7 @@ | |||
385 | 833 | } | 838 | } |
386 | 834 | 839 | ||
387 | 835 | enabled: viewFinderOverlay.readyForCapture && !storageMonitor.diskSpaceCriticallyLow | 840 | enabled: viewFinderOverlay.readyForCapture && !storageMonitor.diskSpaceCriticallyLow |
388 | 841 | && !camera.timedCaptureInProgress | ||
389 | 836 | state: (camera.captureMode == Camera.CaptureVideo) ? | 842 | state: (camera.captureMode == Camera.CaptureVideo) ? |
390 | 837 | ((camera.videoRecorder.recorderState == CameraRecorder.StoppedState) ? "record_off" : "record_on") : | 843 | ((camera.videoRecorder.recorderState == CameraRecorder.StoppedState) ? "record_off" : "record_on") : |
391 | 838 | "camera" | 844 | "camera" |
392 | @@ -869,7 +875,7 @@ | |||
393 | 869 | } | 875 | } |
394 | 870 | 876 | ||
395 | 871 | enabled: !camera.switchInProgress && camera.videoRecorder.recorderState == CameraRecorder.StoppedState | 877 | enabled: !camera.switchInProgress && camera.videoRecorder.recorderState == CameraRecorder.StoppedState |
397 | 872 | && !camera.photoCaptureInProgress | 878 | && !camera.photoCaptureInProgress && !camera.timedCaptureInProgress |
398 | 873 | iconName: "camera-flip" | 879 | iconName: "camera-flip" |
399 | 874 | onClicked: controls.switchCamera() | 880 | onClicked: controls.switchCamera() |
400 | 875 | } | 881 | } |
401 | @@ -892,7 +898,7 @@ | |||
402 | 892 | property real maximumScale: 3.0 | 898 | property real maximumScale: 3.0 |
403 | 893 | property bool active: false | 899 | property bool active: false |
404 | 894 | 900 | ||
406 | 895 | enabled: !camera.photoCaptureInProgress | 901 | enabled: !camera.photoCaptureInProgress && !camera.timedCaptureInProgress |
407 | 896 | onPinchStarted: { | 902 | onPinchStarted: { |
408 | 897 | active = true; | 903 | active = true; |
409 | 898 | initialZoom = zoomControl.value; | 904 | initialZoom = zoomControl.value; |
410 | @@ -910,6 +916,7 @@ | |||
411 | 910 | 916 | ||
412 | 911 | MouseArea { | 917 | MouseArea { |
413 | 912 | id: manualFocusMouseArea | 918 | id: manualFocusMouseArea |
414 | 919 | objectName: "manualFocusMouseArea" | ||
415 | 913 | anchors { | 920 | anchors { |
416 | 914 | fill: parent | 921 | fill: parent |
417 | 915 | // Pinch gestures need more clearance at the edges of the screen, but | 922 | // Pinch gestures need more clearance at the edges of the screen, but |
418 | @@ -918,7 +925,7 @@ | |||
419 | 918 | rightMargin: -bottomEdgeIndicators.height | 925 | rightMargin: -bottomEdgeIndicators.height |
420 | 919 | } | 926 | } |
421 | 920 | enabled: camera.focus.isFocusPointModeSupported(Camera.FocusPointCustom) && | 927 | enabled: camera.focus.isFocusPointModeSupported(Camera.FocusPointCustom) && |
423 | 921 | !camera.photoCaptureInProgress | 928 | !camera.photoCaptureInProgress && !camera.timedCaptureInProgress |
424 | 922 | onClicked: { | 929 | onClicked: { |
425 | 923 | camera.manualFocus(mouse.x, mouse.y); | 930 | camera.manualFocus(mouse.x, mouse.y); |
426 | 924 | mouse.accepted = false; | 931 | mouse.accepted = false; |
427 | 925 | 932 | ||
428 | === modified file 'ViewFinderOverlayLoader.qml' | |||
429 | --- ViewFinderOverlayLoader.qml 2016-02-23 11:46:52 +0000 | |||
430 | +++ ViewFinderOverlayLoader.qml 2016-03-23 15:42:41 +0000 | |||
431 | @@ -25,6 +25,7 @@ | |||
432 | 25 | property var controls: loader.item ? loader.item.controls : null | 25 | property var controls: loader.item ? loader.item.controls : null |
433 | 26 | property var settings: loader.item.settings | 26 | property var settings: loader.item.settings |
434 | 27 | property bool readyForCapture | 27 | property bool readyForCapture |
435 | 28 | property int sensorOrientation | ||
436 | 28 | 29 | ||
437 | 29 | function showFocusRing(x, y) { | 30 | function showFocusRing(x, y) { |
438 | 30 | loader.item.showFocusRing(x, y); | 31 | loader.item.showFocusRing(x, y); |
439 | @@ -37,7 +38,8 @@ | |||
440 | 37 | asynchronous: true | 38 | asynchronous: true |
441 | 38 | Component.onCompleted: { | 39 | Component.onCompleted: { |
442 | 39 | loader.setSource("ViewFinderOverlay.qml", { "camera": loader.camera, | 40 | loader.setSource("ViewFinderOverlay.qml", { "camera": loader.camera, |
444 | 40 | "readyForCapture": Qt.binding(function() { return loader.readyForCapture}) | 41 | "readyForCapture": Qt.binding(function() { return loader.readyForCapture}), |
445 | 42 | "sensorOrientation": Qt.binding(function () {return loader.sensorOrientation}) | ||
446 | 41 | }); | 43 | }); |
447 | 42 | } | 44 | } |
448 | 43 | } | 45 | } |
449 | 44 | 46 | ||
450 | === modified file 'ViewFinderView.qml' | |||
451 | --- ViewFinderView.qml 2016-02-23 11:46:52 +0000 | |||
452 | +++ ViewFinderView.qml 2016-03-23 15:42:41 +0000 | |||
453 | @@ -21,9 +21,9 @@ | |||
454 | 21 | import QtMultimedia 5.0 | 21 | import QtMultimedia 5.0 |
455 | 22 | import CameraApp 0.1 | 22 | import CameraApp 0.1 |
456 | 23 | import QtGraphicalEffects 1.0 | 23 | import QtGraphicalEffects 1.0 |
458 | 24 | import Ubuntu.Content 0.1 | 24 | import Ubuntu.Content 1.3 |
459 | 25 | 25 | ||
461 | 26 | Item { | 26 | FocusScope { |
462 | 27 | id: viewFinderView | 27 | id: viewFinderView |
463 | 28 | 28 | ||
464 | 29 | property bool overlayVisible: true | 29 | property bool overlayVisible: true |
465 | @@ -60,11 +60,15 @@ | |||
466 | 60 | property bool failedToConnect: false | 60 | property bool failedToConnect: false |
467 | 61 | 61 | ||
468 | 62 | function manualFocus(x, y) { | 62 | function manualFocus(x, y) { |
474 | 63 | viewFinderOverlay.showFocusRing(x, y); | 63 | var normalizedPoint = viewFinder.mapPointToSourceNormalized(Qt.point(x, y - viewFinder.y)); |
475 | 64 | autoFocusTimer.restart(); | 64 | if (normalizedPoint.x >= 0.0 && normalizedPoint.x <= 1.0 && |
476 | 65 | focus.focusMode = Camera.FocusAuto; | 65 | normalizedPoint.y >= 0.0 && normalizedPoint.y <= 1.0) { |
477 | 66 | focus.customFocusPoint = viewFinder.mapPointToSourceNormalized(Qt.point(x, y)); | 66 | viewFinderOverlay.showFocusRing(x, y); |
478 | 67 | focus.focusPointMode = Camera.FocusPointCustom; | 67 | autoFocusTimer.restart(); |
479 | 68 | focus.focusMode = Camera.FocusAuto; | ||
480 | 69 | focus.customFocusPoint = normalizedPoint; | ||
481 | 70 | focus.focusPointMode = Camera.FocusPointCustom; | ||
482 | 71 | } | ||
483 | 68 | } | 72 | } |
484 | 69 | 73 | ||
485 | 70 | function autoFocus() { | 74 | function autoFocus() { |
486 | @@ -96,6 +100,7 @@ | |||
487 | 96 | property alias maximumZoom: camera.maximumDigitalZoom | 100 | property alias maximumZoom: camera.maximumDigitalZoom |
488 | 97 | property bool switchInProgress: false | 101 | property bool switchInProgress: false |
489 | 98 | property bool photoCaptureInProgress: false | 102 | property bool photoCaptureInProgress: false |
490 | 103 | property bool timedCaptureInProgress: false | ||
491 | 99 | 104 | ||
492 | 100 | onPhotoCaptureInProgressChanged: { | 105 | onPhotoCaptureInProgressChanged: { |
493 | 101 | if (main.contentExportMode && camera.photoCaptureInProgress) { | 106 | if (main.contentExportMode && camera.photoCaptureInProgress) { |
494 | @@ -110,14 +115,6 @@ | |||
495 | 110 | if (photoRollHint.necessary && !main.transfer) photoRollHint.enable(); | 115 | if (photoRollHint.necessary && !main.transfer) photoRollHint.enable(); |
496 | 111 | camera.photoCaptureInProgress = false; | 116 | camera.photoCaptureInProgress = false; |
497 | 112 | } | 117 | } |
498 | 113 | |||
499 | 114 | if (main.transfer) { | ||
500 | 115 | if (main.transfer.contentType === ContentType.Videos) { | ||
501 | 116 | viewFinderView.captureMode = Camera.CaptureVideo; | ||
502 | 117 | } else { | ||
503 | 118 | viewFinderView.captureMode = Camera.CaptureStillImage; | ||
504 | 119 | } | ||
505 | 120 | } | ||
506 | 121 | } | 118 | } |
507 | 122 | } | 119 | } |
508 | 123 | 120 | ||
509 | @@ -265,6 +262,7 @@ | |||
510 | 265 | // Set orientation only at startup because later on Screen.primaryOrientation | 262 | // Set orientation only at startup because later on Screen.primaryOrientation |
511 | 266 | // may change. | 263 | // may change. |
512 | 267 | orientation = Screen.primaryOrientation === Qt.PortraitOrientation ? -90 : 0; | 264 | orientation = Screen.primaryOrientation === Qt.PortraitOrientation ? -90 : 0; |
513 | 265 | viewFinderOverlay.sensorOrientation = orientation; | ||
514 | 268 | } | 266 | } |
515 | 269 | 267 | ||
516 | 270 | transform: Rotation { | 268 | transform: Rotation { |
517 | @@ -421,6 +419,7 @@ | |||
518 | 421 | 419 | ||
519 | 422 | PhotoRollHint { | 420 | PhotoRollHint { |
520 | 423 | id: photoRollHint | 421 | id: photoRollHint |
521 | 422 | objectName: "photoRollHint" | ||
522 | 424 | anchors.fill: parent | 423 | anchors.fill: parent |
523 | 425 | visible: enabled | 424 | visible: enabled |
524 | 426 | 425 | ||
525 | 427 | 426 | ||
526 | === modified file 'camera-app.qml' | |||
527 | --- camera-app.qml 2016-02-23 11:46:52 +0000 | |||
528 | +++ camera-app.qml 2016-03-23 15:42:41 +0000 | |||
529 | @@ -20,7 +20,7 @@ | |||
530 | 20 | import Ubuntu.Components 1.3 | 20 | import Ubuntu.Components 1.3 |
531 | 21 | import Ubuntu.Unity.Action 1.1 as UnityActions | 21 | import Ubuntu.Unity.Action 1.1 as UnityActions |
532 | 22 | import UserMetrics 0.1 | 22 | import UserMetrics 0.1 |
534 | 23 | import Ubuntu.Content 0.1 | 23 | import Ubuntu.Content 1.3 |
535 | 24 | import CameraApp 0.1 | 24 | import CameraApp 0.1 |
536 | 25 | 25 | ||
537 | 26 | Window { | 26 | Window { |
538 | @@ -30,6 +30,26 @@ | |||
539 | 30 | height: units.gu(80) | 30 | height: units.gu(80) |
540 | 31 | color: "black" | 31 | color: "black" |
541 | 32 | title: "Camera" | 32 | title: "Camera" |
542 | 33 | // special flag only supported by Unity8/MIR so far that hides the shell's | ||
543 | 34 | // top panel in Staged mode | ||
544 | 35 | flags: Qt.Window | 0x00800000 | ||
545 | 36 | |||
546 | 37 | property int preFullScreenVisibility | ||
547 | 38 | |||
548 | 39 | function toggleFullScreen() { | ||
549 | 40 | if (main.visibility != Window.FullScreen) { | ||
550 | 41 | preFullScreenVisibility = main.visibility; | ||
551 | 42 | main.visibility = Window.FullScreen; | ||
552 | 43 | } else { | ||
553 | 44 | main.visibility = preFullScreenVisibility; | ||
554 | 45 | } | ||
555 | 46 | } | ||
556 | 47 | |||
557 | 48 | function exitFullScreen() { | ||
558 | 49 | if (main.visibility == Window.FullScreen) { | ||
559 | 50 | main.visibility = preFullScreenVisibility; | ||
560 | 51 | } | ||
561 | 52 | } | ||
562 | 33 | 53 | ||
563 | 34 | UnityActions.ActionManager { | 54 | UnityActions.ActionManager { |
564 | 35 | actions: [ | 55 | actions: [ |
565 | @@ -63,11 +83,7 @@ | |||
566 | 63 | 83 | ||
567 | 64 | Component.onCompleted: { | 84 | Component.onCompleted: { |
568 | 65 | i18n.domain = "camera-app"; | 85 | i18n.domain = "camera-app"; |
574 | 66 | if (!application.desktopMode) { | 86 | main.show(); |
570 | 67 | main.showFullScreen(); | ||
571 | 68 | } else { | ||
572 | 69 | main.show(); | ||
573 | 70 | } | ||
575 | 71 | } | 87 | } |
576 | 72 | 88 | ||
577 | 73 | 89 | ||
578 | @@ -78,6 +94,15 @@ | |||
579 | 78 | flickableDirection: state == "PORTRAIT" ? Flickable.HorizontalFlick : Flickable.VerticalFlick | 94 | flickableDirection: state == "PORTRAIT" ? Flickable.HorizontalFlick : Flickable.VerticalFlick |
580 | 79 | boundsBehavior: Flickable.StopAtBounds | 95 | boundsBehavior: Flickable.StopAtBounds |
581 | 80 | 96 | ||
582 | 97 | Keys.onPressed: { | ||
583 | 98 | if (event.key == Qt.Key_F11) { | ||
584 | 99 | main.toggleFullScreen(); | ||
585 | 100 | event.accepted = true; | ||
586 | 101 | } | ||
587 | 102 | } | ||
588 | 103 | Keys.onEscapePressed: main.exitFullScreen() | ||
589 | 104 | |||
590 | 105 | |||
591 | 81 | property real panesMargin: units.gu(1) | 106 | property real panesMargin: units.gu(1) |
592 | 82 | property real ratio | 107 | property real ratio |
593 | 83 | property int orientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation) | 108 | property int orientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation) |
594 | @@ -135,7 +160,9 @@ | |||
595 | 135 | } | 160 | } |
596 | 136 | } | 161 | } |
597 | 137 | ] | 162 | ] |
599 | 138 | interactive: !viewFinderView.touchAcquired && !galleryView.touchAcquired && !viewFinderView.camera.photoCaptureInProgress | 163 | interactive: !viewFinderView.touchAcquired && !galleryView.touchAcquired |
600 | 164 | && !viewFinderView.camera.photoCaptureInProgress | ||
601 | 165 | && !viewFinderView.camera.timedCaptureInProgress | ||
602 | 139 | 166 | ||
603 | 140 | Component.onCompleted: { | 167 | Component.onCompleted: { |
604 | 141 | // FIXME: workaround for qtubuntu not returning values depending on the grid unit definition | 168 | // FIXME: workaround for qtubuntu not returning values depending on the grid unit definition |
605 | @@ -256,6 +283,7 @@ | |||
606 | 256 | height: viewSwitcher.height | 283 | height: viewSwitcher.height |
607 | 257 | overlayVisible: !viewSwitcher.moving && !viewSwitcher.flicking | 284 | overlayVisible: !viewSwitcher.moving && !viewSwitcher.flicking |
608 | 258 | inView: viewSwitcher.ratio < 0.5 | 285 | inView: viewSwitcher.ratio < 0.5 |
609 | 286 | focus: !galleryView.focus | ||
610 | 259 | opacity: inView ? 1.0 : 0.0 | 287 | opacity: inView ? 1.0 : 0.0 |
611 | 260 | onPhotoTaken: { | 288 | onPhotoTaken: { |
612 | 261 | galleryView.prependMediaToModel(filePath); | 289 | galleryView.prependMediaToModel(filePath); |
613 | @@ -273,6 +301,7 @@ | |||
614 | 273 | width: viewSwitcher.width | 301 | width: viewSwitcher.width |
615 | 274 | height: viewSwitcher.height | 302 | height: viewSwitcher.height |
616 | 275 | inView: viewSwitcher.ratio > 0.0 | 303 | inView: viewSwitcher.ratio > 0.0 |
617 | 304 | focus: inView | ||
618 | 276 | onExit: viewSwitcher.switchToViewFinder() | 305 | onExit: viewSwitcher.switchToViewFinder() |
619 | 277 | opacity: inView ? 1.0 : 0.0 | 306 | opacity: inView ? 1.0 : 0.0 |
620 | 278 | } | 307 | } |
621 | @@ -280,7 +309,7 @@ | |||
622 | 280 | 309 | ||
623 | 281 | property bool contentExportMode: transfer !== null | 310 | property bool contentExportMode: transfer !== null |
624 | 282 | property var transfer: null | 311 | property var transfer: null |
626 | 283 | property var transferContentType: ContentType.Pictures | 312 | property var transferContentType: transfer ? transfer.contentType : "image" |
627 | 284 | 313 | ||
628 | 285 | function exportContent(urls) { | 314 | function exportContent(urls) { |
629 | 286 | if (!main.transfer) return; | 315 | if (!main.transfer) return; |
630 | @@ -312,16 +341,10 @@ | |||
631 | 312 | onExportRequested: { | 341 | onExportRequested: { |
632 | 313 | viewSwitcher.switchToViewFinder(); | 342 | viewSwitcher.switchToViewFinder(); |
633 | 314 | 343 | ||
644 | 315 | // The exportRequested event can arrive before or after the | 344 | if (transfer.contentType === ContentType.Videos) { |
645 | 316 | // app is active, but setting the recording type before the | 345 | viewFinderView.captureMode = Camera.CaptureVideo; |
646 | 317 | // capture becomes ready does not have any effect. | 346 | } else { |
647 | 318 | // See camera.imageCapture.onReadyChanged for the other case. | 347 | viewFinderView.captureMode = Camera.CaptureStillImage; |
638 | 319 | if (viewFinderView.camera.imageCapture.ready) { | ||
639 | 320 | if (transfer.contentType === ContentType.Videos) { | ||
640 | 321 | viewFinderView.captureMode = Camera.CaptureVideo; | ||
641 | 322 | } else { | ||
642 | 323 | viewFinderView.captureMode = Camera.CaptureStillImage; | ||
643 | 324 | } | ||
648 | 325 | } | 348 | } |
649 | 326 | main.transfer = transfer; | 349 | main.transfer = transfer; |
650 | 327 | } | 350 | } |
651 | 328 | 351 | ||
652 | === modified file 'debian/control' | |||
653 | --- debian/control 2015-07-31 02:23:54 +0000 | |||
654 | +++ debian/control 2016-03-23 15:42:41 +0000 | |||
655 | @@ -13,11 +13,14 @@ | |||
656 | 13 | qtbase5-dev, | 13 | qtbase5-dev, |
657 | 14 | qtdeclarative5-dev, | 14 | qtdeclarative5-dev, |
658 | 15 | qml-module-qtquick2, | 15 | qml-module-qtquick2, |
659 | 16 | qml-module-qtpositioning, | ||
660 | 16 | qml-module-qttest, | 17 | qml-module-qttest, |
661 | 17 | qtdeclarative5-ubuntu-ui-toolkit-plugin, | 18 | qtdeclarative5-ubuntu-ui-toolkit-plugin, |
662 | 18 | qtdeclarative5-unity-action-plugin (>= 1.1.0), | 19 | qtdeclarative5-unity-action-plugin (>= 1.1.0), |
663 | 19 | qtdeclarative5-usermetrics0.1, | 20 | qtdeclarative5-usermetrics0.1, |
664 | 20 | qtdeclarative5-ubuntu-content1, | 21 | qtdeclarative5-ubuntu-content1, |
665 | 22 | qtdeclarative5-ubuntu-thumbnailer0.1, | ||
666 | 23 | qtdeclarative5-ubuntu-ui-extras0.2, | ||
667 | 21 | qtmultimedia5-dev, | 24 | qtmultimedia5-dev, |
668 | 22 | libusermetricsinput1-dev, | 25 | libusermetricsinput1-dev, |
669 | 23 | gettext, | 26 | gettext, |
670 | 24 | 27 | ||
671 | === modified file 'tests/autopilot/camera_app/emulators/main_window.py' | |||
672 | --- tests/autopilot/camera_app/emulators/main_window.py 2015-11-25 17:00:31 +0000 | |||
673 | +++ tests/autopilot/camera_app/emulators/main_window.py 2016-03-23 15:42:41 +0000 | |||
674 | @@ -36,10 +36,29 @@ | |||
675 | 36 | """Returns the gallery view""" | 36 | """Returns the gallery view""" |
676 | 37 | return self.app.wait_select_single("GalleryView") | 37 | return self.app.wait_select_single("GalleryView") |
677 | 38 | 38 | ||
678 | 39 | def get_media(self, index=0): | ||
679 | 40 | """Returns media at index in the currently loaded view in gallery""" | ||
680 | 41 | gallery = self.get_gallery() | ||
681 | 42 | view = gallery.select_single("SlideshowView") | ||
682 | 43 | if not view.visible: | ||
683 | 44 | view = gallery.select_single("PhotogridView") | ||
684 | 45 | |||
685 | 46 | return view.wait_select_single(objectName="mediaItem" + str(index)) | ||
686 | 47 | |||
687 | 48 | def get_broken_media_icon(self, index=0): | ||
688 | 49 | """Returns the broken media icon""" | ||
689 | 50 | media = self.get_media(index) | ||
690 | 51 | return media.wait_select_single(objectName="thumbnailLoadingErrorIcon") | ||
691 | 52 | |||
692 | 39 | def get_no_media_hint(self): | 53 | def get_no_media_hint(self): |
693 | 40 | """Returns the Item representing the hint that no media is available""" | 54 | """Returns the Item representing the hint that no media is available""" |
694 | 41 | return self.app.wait_select_single(objectName="noMediaHint") | 55 | return self.app.wait_select_single(objectName="noMediaHint") |
695 | 42 | 56 | ||
696 | 57 | def get_focus_mouse_area(self): | ||
697 | 58 | """Returns the focus mouse area""" | ||
698 | 59 | return self.app.wait_select_single("QQuickMouseArea", | ||
699 | 60 | objectName="manualFocusMouseArea") | ||
700 | 61 | |||
701 | 43 | def get_focus_ring(self): | 62 | def get_focus_ring(self): |
702 | 44 | """Returns the focus ring of the camera""" | 63 | """Returns the focus ring of the camera""" |
703 | 45 | return self.app.wait_select_single("FocusRing") | 64 | return self.app.wait_select_single("FocusRing") |
704 | @@ -49,9 +68,9 @@ | |||
705 | 49 | return self.app.wait_select_single("ShootButton") | 68 | return self.app.wait_select_single("ShootButton") |
706 | 50 | 69 | ||
707 | 51 | def get_photo_roll_hint(self): | 70 | def get_photo_roll_hint(self): |
711 | 52 | """Returns the layer that serves at hinting to the existence of the | 71 | """Returns the photo roll hint""" |
712 | 53 | photo roll""" | 72 | return self.app.wait_select_single("PhotoRollHint", |
713 | 54 | return self.app.wait_select_single("PhotoRollHint") | 73 | objectName="photoRollHint") |
714 | 55 | 74 | ||
715 | 56 | def get_record_control(self): | 75 | def get_record_control(self): |
716 | 57 | """Returns the button that toggles between photo and video recording""" | 76 | """Returns the button that toggles between photo and video recording""" |
717 | @@ -63,8 +82,12 @@ | |||
718 | 63 | in settingsProperty | 82 | in settingsProperty |
719 | 64 | """ | 83 | """ |
720 | 65 | optionButtons = self.app.select_many("OptionButton") | 84 | optionButtons = self.app.select_many("OptionButton") |
723 | 66 | return next(button for button in optionButtons | 85 | optionButton = next(button for button in optionButtons |
724 | 67 | if button.settingsProperty == settingsProperty) | 86 | if button.settingsProperty == settingsProperty) |
725 | 87 | if optionButton.visible: | ||
726 | 88 | return optionButton | ||
727 | 89 | else: | ||
728 | 90 | return None | ||
729 | 68 | 91 | ||
730 | 69 | def get_flash_button(self): | 92 | def get_flash_button(self): |
731 | 70 | """Returns the flash control button of the camera""" | 93 | """Returns the flash control button of the camera""" |
732 | @@ -90,6 +113,10 @@ | |||
733 | 90 | """Returns the video resolution button of the camera""" | 113 | """Returns the video resolution button of the camera""" |
734 | 91 | return self.get_option_button("videoResolution") | 114 | return self.get_option_button("videoResolution") |
735 | 92 | 115 | ||
736 | 116 | def get_timer_delay_button(self): | ||
737 | 117 | """Returns the timer delay option button of the camera""" | ||
738 | 118 | return self.get_option_button("selfTimerDelay") | ||
739 | 119 | |||
740 | 93 | def get_stop_watch(self): | 120 | def get_stop_watch(self): |
741 | 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""" |
742 | 95 | return self.app.wait_select_single("StopWatch") | 122 | return self.app.wait_select_single("StopWatch") |
743 | @@ -139,30 +166,71 @@ | |||
744 | 139 | except: | 166 | except: |
745 | 140 | return None | 167 | return None |
746 | 141 | 168 | ||
747 | 169 | def open_actions_drawer(self, gallery): | ||
748 | 170 | """Opens action drawer of gallery""" | ||
749 | 171 | actionsDrawerButton = gallery.wait_select_single( | ||
750 | 172 | "IconButton", | ||
751 | 173 | objectName="additionalActionsButton") | ||
752 | 174 | self.app.pointing_device.move_to_object(actionsDrawerButton) | ||
753 | 175 | self.app.pointing_device.click() | ||
754 | 176 | actionsDrawer = gallery.wait_select_single("QQuickItem", | ||
755 | 177 | objectName="actionsDrawer") | ||
756 | 178 | actionsDrawer.fullyOpened.wait_for(True) | ||
757 | 179 | |||
758 | 180 | def close_actions_drawer(self, gallery): | ||
759 | 181 | """Closes action drawer of gallery""" | ||
760 | 182 | actionsDrawerButton = gallery.wait_select_single( | ||
761 | 183 | "IconButton", | ||
762 | 184 | objectName="additionalActionsButton") | ||
763 | 185 | self.app.pointing_device.move_to_object(actionsDrawerButton) | ||
764 | 186 | self.app.pointing_device.click() | ||
765 | 187 | actionsDrawer = gallery.wait_select_single("QQuickItem", | ||
766 | 188 | objectName="actionsDrawer") | ||
767 | 189 | actionsDrawer.fullyClosed.wait_for(True) | ||
768 | 190 | |||
769 | 142 | def swipe_to_gallery(self, testCase): | 191 | def swipe_to_gallery(self, testCase): |
770 | 143 | view_switcher = self.get_view_switcher() | 192 | view_switcher = self.get_view_switcher() |
771 | 193 | viewfinder = self.get_viewfinder() | ||
772 | 194 | view_switcher.interactive.wait_for(True) | ||
773 | 195 | view_switcher.enabled.wait_for(True) | ||
774 | 196 | view_switcher.settling.wait_for(False) | ||
775 | 197 | view_switcher.switching.wait_for(False) | ||
776 | 198 | viewfinder.inView.wait_for(True) | ||
777 | 144 | x, y = view_switcher.x, view_switcher.y | 199 | x, y = view_switcher.x, view_switcher.y |
778 | 145 | w, h = view_switcher.width, view_switcher.height | 200 | w, h = view_switcher.width, view_switcher.height |
779 | 146 | 201 | ||
780 | 147 | tx = x + (w // 2) | 202 | tx = x + (w // 2) |
781 | 148 | ty = y + (h // 2) | 203 | ty = y + (h // 2) |
782 | 149 | 204 | ||
785 | 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 |
786 | 151 | viewfinder = self.get_viewfinder() | 206 | self.app.pointing_device.drag(tx, ty, x, ty, rate=1, |
787 | 207 | time_between_events=0.0001) | ||
788 | 208 | |||
789 | 152 | testCase.assertThat(viewfinder.inView, Eventually(Equals(False))) | 209 | testCase.assertThat(viewfinder.inView, Eventually(Equals(False))) |
790 | 210 | view_switcher.settling.wait_for(False) | ||
791 | 211 | view_switcher.switching.wait_for(False) | ||
792 | 153 | 212 | ||
793 | 154 | def swipe_to_viewfinder(self, testCase): | 213 | def swipe_to_viewfinder(self, testCase): |
794 | 155 | view_switcher = self.get_view_switcher() | 214 | view_switcher = self.get_view_switcher() |
795 | 215 | viewfinder = self.get_viewfinder() | ||
796 | 216 | view_switcher.interactive.wait_for(True) | ||
797 | 217 | view_switcher.enabled.wait_for(True) | ||
798 | 218 | view_switcher.settling.wait_for(False) | ||
799 | 219 | view_switcher.switching.wait_for(False) | ||
800 | 220 | viewfinder.inView.wait_for(False) | ||
801 | 156 | x, y = view_switcher.x, view_switcher.y | 221 | x, y = view_switcher.x, view_switcher.y |
802 | 157 | w, h = view_switcher.width, view_switcher.height | 222 | w, h = view_switcher.width, view_switcher.height |
803 | 158 | 223 | ||
804 | 159 | tx = x + (w // 2) | 224 | tx = x + (w // 2) |
805 | 160 | ty = y + (h // 2) | 225 | ty = y + (h // 2) |
806 | 161 | 226 | ||
807 | 227 | # FIXME: a rate higher than 1 does not always make view_switcher move | ||
808 | 162 | self.app.pointing_device.drag( | 228 | self.app.pointing_device.drag( |
811 | 163 | tx, ty, (tx + view_switcher.width // 2), ty, rate=1) | 229 | tx, ty, (tx + view_switcher.width // 2), ty, rate=1, |
812 | 164 | viewfinder = self.get_viewfinder() | 230 | time_between_events=0.0001) |
813 | 165 | testCase.assertThat(viewfinder.inView, Eventually(Equals(True))) | 231 | testCase.assertThat(viewfinder.inView, Eventually(Equals(True))) |
814 | 232 | view_switcher.settling.wait_for(False) | ||
815 | 233 | view_switcher.switching.wait_for(False) | ||
816 | 166 | 234 | ||
817 | 167 | def switch_cameras(self): | 235 | def switch_cameras(self): |
818 | 168 | # Swap cameras and wait for camera to settle | 236 | # Swap cameras and wait for camera to settle |
819 | 169 | 237 | ||
820 | === modified file 'tests/autopilot/camera_app/emulators/panel.py' | |||
821 | --- tests/autopilot/camera_app/emulators/panel.py 2015-12-01 09:03:34 +0000 | |||
822 | +++ tests/autopilot/camera_app/emulators/panel.py 2016-03-23 15:42:41 +0000 | |||
823 | @@ -24,6 +24,7 @@ | |||
824 | 24 | :return: The panel. | 24 | :return: The panel. |
825 | 25 | 25 | ||
826 | 26 | """ | 26 | """ |
827 | 27 | self.ready.wait_for(True) | ||
828 | 27 | self.animating.wait_for(False) | 28 | self.animating.wait_for(False) |
829 | 28 | if not self.opened: | 29 | if not self.opened: |
830 | 29 | self._drag_to_open() | 30 | self._drag_to_open() |
831 | @@ -38,11 +39,14 @@ | |||
832 | 38 | start_y = y + self.height - 1 | 39 | start_y = y + self.height - 1 |
833 | 39 | stop_y = y | 40 | stop_y = y |
834 | 40 | 41 | ||
836 | 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 |
837 | 43 | self.pointing_device.drag(line_x, start_y, line_x, stop_y, rate=1, | ||
838 | 44 | time_between_events=0.0001) | ||
839 | 42 | 45 | ||
840 | 43 | @autopilot_logging.log_action(logger.info) | 46 | @autopilot_logging.log_action(logger.info) |
841 | 44 | def close(self): | 47 | def close(self): |
842 | 45 | """Close the panel if it's opened.""" | 48 | """Close the panel if it's opened.""" |
843 | 49 | self.ready.wait_for(True) | ||
844 | 46 | self.animating.wait_for(False) | 50 | self.animating.wait_for(False) |
845 | 47 | if self.opened: | 51 | if self.opened: |
846 | 48 | self._drag_to_close() | 52 | self._drag_to_close() |
847 | @@ -55,4 +59,6 @@ | |||
848 | 55 | start_y = y | 59 | start_y = y |
849 | 56 | stop_y = y + self.height - 1 | 60 | stop_y = y + self.height - 1 |
850 | 57 | 61 | ||
852 | 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 |
853 | 63 | self.pointing_device.drag(line_x, start_y, line_x, stop_y, rate=1, | ||
854 | 64 | time_between_events=0.0001) | ||
855 | 59 | 65 | ||
856 | === modified file 'tests/autopilot/camera_app/tests/__init__.py' | |||
857 | --- tests/autopilot/camera_app/tests/__init__.py 2015-05-15 07:29:16 +0000 | |||
858 | +++ tests/autopilot/camera_app/tests/__init__.py 2016-03-23 15:42:41 +0000 | |||
859 | @@ -8,7 +8,6 @@ | |||
860 | 8 | """Camera-app autopilot tests.""" | 8 | """Camera-app autopilot tests.""" |
861 | 9 | 9 | ||
862 | 10 | import os | 10 | import os |
863 | 11 | import time | ||
864 | 12 | import shutil | 11 | import shutil |
865 | 13 | from pkg_resources import resource_filename | 12 | from pkg_resources import resource_filename |
866 | 14 | 13 | ||
867 | @@ -42,6 +41,12 @@ | |||
868 | 42 | sample_dir = resource_filename('camera_app', 'data') | 41 | sample_dir = resource_filename('camera_app', 'data') |
869 | 43 | 42 | ||
870 | 44 | def setUp(self): | 43 | def setUp(self): |
871 | 44 | # Remove configuration file | ||
872 | 45 | config_file = os.path.expanduser( | ||
873 | 46 | "~/.config/com.ubuntu.camera/com.ubuntu.camera.conf") | ||
874 | 47 | if os.path.exists(config_file): | ||
875 | 48 | os.remove(config_file) | ||
876 | 49 | |||
877 | 45 | self.pointing_device = Pointer(self.input_device_class.create()) | 50 | self.pointing_device = Pointer(self.input_device_class.create()) |
878 | 46 | super(CameraAppTestCase, self).setUp() | 51 | super(CameraAppTestCase, self).setUp() |
879 | 47 | if os.path.exists(self.local_location): | 52 | if os.path.exists(self.local_location): |
880 | @@ -51,11 +56,7 @@ | |||
881 | 51 | else: | 56 | else: |
882 | 52 | self.launch_click_installed() | 57 | self.launch_click_installed() |
883 | 53 | 58 | ||
884 | 54 | # wait and sleep as workaround for bug #1373039. To | ||
885 | 55 | # make sure large components get loaded asynchronously on start-up | ||
886 | 56 | # -- Chris Gagnon 11-17-2014 | ||
887 | 57 | self.main_window.get_qml_view().visible.wait_for(True) | 59 | self.main_window.get_qml_view().visible.wait_for(True) |
888 | 58 | time.sleep(5) | ||
889 | 59 | 60 | ||
890 | 60 | def launch_test_local(self): | 61 | def launch_test_local(self): |
891 | 61 | self.app = self.launch_test_application( | 62 | self.app = self.launch_test_application( |
892 | @@ -107,6 +108,11 @@ | |||
893 | 107 | shutil.copyfile(os.path.join(self.sample_dir, "sample.jpg"), | 108 | shutil.copyfile(os.path.join(self.sample_dir, "sample.jpg"), |
894 | 108 | os.path.join(self.pictures_dir, "sample.jpg")) | 109 | os.path.join(self.pictures_dir, "sample.jpg")) |
895 | 109 | 110 | ||
899 | 110 | def add_sample_video(self): | 111 | def add_sample_video(self, broken=False): |
900 | 111 | shutil.copyfile(os.path.join(self.sample_dir, "sample.mp4"), | 112 | if broken: |
901 | 112 | os.path.join(self.videos_dir, "sample.mp4")) | 113 | path = os.path.join(self.videos_dir, "sample_broken.mp4") |
902 | 114 | with open(path, "w") as video: | ||
903 | 115 | video.write("I AM NOT A VIDEO") | ||
904 | 116 | else: | ||
905 | 117 | shutil.copyfile(os.path.join(self.sample_dir, "sample.mp4"), | ||
906 | 118 | os.path.join(self.videos_dir, "sample.mp4")) | ||
907 | 113 | 119 | ||
908 | === modified file 'tests/autopilot/camera_app/tests/test_capture.py' | |||
909 | --- tests/autopilot/camera_app/tests/test_capture.py 2015-12-01 09:03:34 +0000 | |||
910 | +++ tests/autopilot/camera_app/tests/test_capture.py 2016-03-23 15:42:41 +0000 | |||
911 | @@ -17,6 +17,7 @@ | |||
912 | 17 | import unittest | 17 | import unittest |
913 | 18 | import time | 18 | import time |
914 | 19 | import os | 19 | import os |
915 | 20 | import glob | ||
916 | 20 | 21 | ||
917 | 21 | 22 | ||
918 | 22 | class TestCapture(CameraAppTestCase): | 23 | class TestCapture(CameraAppTestCase): |
919 | @@ -25,23 +26,10 @@ | |||
920 | 25 | """ This is needed to wait for the application to start. | 26 | """ This is needed to wait for the application to start. |
921 | 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.""" |
922 | 27 | def setUp(self): | 28 | def setUp(self): |
923 | 28 | # Remove configuration file where knowledge of the photo roll hint's | ||
924 | 29 | # necessity is stored | ||
925 | 30 | config_file = os.path.expanduser( | ||
926 | 31 | "~/.config/com.ubuntu.camera/com.ubuntu.camera.conf") | ||
927 | 32 | if os.path.exists(config_file): | ||
928 | 33 | os.remove(config_file) | ||
929 | 34 | |||
930 | 35 | super(TestCapture, self).setUp() | 29 | super(TestCapture, self).setUp() |
931 | 36 | |||
932 | 37 | self.assertThat( | ||
933 | 38 | self.main_window.get_qml_view().visible, Eventually(Equals(True))) | ||
934 | 39 | self.pictures_dir = os.path.expanduser("~/Pictures/com.ubuntu.camera") | 30 | self.pictures_dir = os.path.expanduser("~/Pictures/com.ubuntu.camera") |
935 | 40 | self.videos_dir = os.path.expanduser("~/Videos/com.ubuntu.camera") | 31 | self.videos_dir = os.path.expanduser("~/Videos/com.ubuntu.camera") |
936 | 41 | 32 | ||
937 | 42 | def tearDown(self): | ||
938 | 43 | super(TestCapture, self).tearDown() | ||
939 | 44 | |||
940 | 45 | """Test taking a picture""" | 33 | """Test taking a picture""" |
941 | 46 | def test_take_picture(self): | 34 | def test_take_picture(self): |
942 | 47 | exposure_button = self.main_window.get_exposure_button() | 35 | exposure_button = self.main_window.get_exposure_button() |
943 | @@ -83,6 +71,62 @@ | |||
944 | 83 | # check that the camera is able to capture another photo | 71 | # check that the camera is able to capture another photo |
945 | 84 | self.assertThat(exposure_button.enabled, Eventually(Equals(True))) | 72 | self.assertThat(exposure_button.enabled, Eventually(Equals(True))) |
946 | 85 | 73 | ||
947 | 74 | """Test taking a picture with a timer set""" | ||
948 | 75 | def test_take_picture_with_timer(self): | ||
949 | 76 | delay = 5 | ||
950 | 77 | self.enable_timer("%s seconds" % str(delay)) | ||
951 | 78 | |||
952 | 79 | # start timed shoot | ||
953 | 80 | shoot_button = self.main_window.get_exposure_button() | ||
954 | 81 | self.assertThat(shoot_button.enabled, Eventually(Equals(True))) | ||
955 | 82 | self.pointing_device.move_to_object(shoot_button) | ||
956 | 83 | self.pointing_device.click() | ||
957 | 84 | |||
958 | 85 | switch_cameras_button = self.main_window.get_swap_camera_button() | ||
959 | 86 | record_mode_button = self.main_window.get_record_control() | ||
960 | 87 | view_switcher = self.main_window.get_view_switcher() | ||
961 | 88 | |||
962 | 89 | # controls and navigation should be disabled at this point | ||
963 | 90 | self.assertThat(shoot_button.enabled, | ||
964 | 91 | Eventually(Equals(True))) | ||
965 | 92 | self.assertThat(switch_cameras_button.enabled, | ||
966 | 93 | Eventually(Equals(True))) | ||
967 | 94 | self.assertThat(record_mode_button.enabled, | ||
968 | 95 | Eventually(Equals(True))) | ||
969 | 96 | self.assertThat(view_switcher.interactive, | ||
970 | 97 | Eventually(Equals(True))) | ||
971 | 98 | |||
972 | 99 | # after the delay controls and navigation should be re-enabled | ||
973 | 100 | self.assertThat(shoot_button.enabled, | ||
974 | 101 | Eventually(Equals(True), timeout=delay)) | ||
975 | 102 | self.assertThat(switch_cameras_button.enabled, | ||
976 | 103 | Eventually(Equals(True), timeout=delay)) | ||
977 | 104 | self.assertThat(record_mode_button.enabled, | ||
978 | 105 | Eventually(Equals(True), timeout=delay)) | ||
979 | 106 | self.assertThat(view_switcher.interactive, | ||
980 | 107 | Eventually(Equals(True), timeout=delay)) | ||
981 | 108 | |||
982 | 109 | def enable_timer(self, label_value): | ||
983 | 110 | # open bottom edge | ||
984 | 111 | bottom_edge = self.main_window.get_bottom_edge() | ||
985 | 112 | bottom_edge.open() | ||
986 | 113 | |||
987 | 114 | # open video resolution option value selector showing the possible | ||
988 | 115 | # values | ||
989 | 116 | timer_delay_button = self.main_window.get_timer_delay_button() | ||
990 | 117 | self.pointing_device.move_to_object(timer_delay_button) | ||
991 | 118 | self.pointing_device.click() | ||
992 | 119 | option_value_selector = self.main_window.get_option_value_selector() | ||
993 | 120 | self.assertThat( | ||
994 | 121 | option_value_selector.visible, Eventually(Equals(True))) | ||
995 | 122 | |||
996 | 123 | # select a 5 seconds delay | ||
997 | 124 | option = self.main_window.get_option_value_button(label_value) | ||
998 | 125 | self.pointing_device.move_to_object(option) | ||
999 | 126 | self.pointing_device.click() | ||
1000 | 127 | |||
1001 | 128 | bottom_edge.close() | ||
1002 | 129 | |||
1003 | 86 | def test_record_video(self): | 130 | def test_record_video(self): |
1004 | 87 | """Test clicking on the record control. | 131 | """Test clicking on the record control. |
1005 | 88 | 132 | ||
1006 | @@ -208,7 +252,7 @@ | |||
1007 | 208 | def get_first_picture(self, timeout=10): | 252 | def get_first_picture(self, timeout=10): |
1008 | 209 | pictures = [] | 253 | pictures = [] |
1009 | 210 | for i in range(0, timeout): | 254 | for i in range(0, timeout): |
1011 | 211 | pictures = os.listdir(self.pictures_dir) | 255 | pictures = glob.glob(os.path.join(self.pictures_dir, "*.jpg")) |
1012 | 212 | if len(pictures) != 0: | 256 | if len(pictures) != 0: |
1013 | 213 | break | 257 | break |
1014 | 214 | time.sleep(1) | 258 | time.sleep(1) |
1015 | @@ -236,9 +280,11 @@ | |||
1016 | 236 | return quality | 280 | return quality |
1017 | 237 | 281 | ||
1018 | 238 | def dismiss_first_photo_hint(self): | 282 | def dismiss_first_photo_hint(self): |
1022 | 239 | # Swipe to photo roll and back to viewfinder | 283 | photo_roll_hint = self.main_window.get_photo_roll_hint() |
1023 | 240 | self.main_window.swipe_to_gallery(self) | 284 | if photo_roll_hint.enabled: |
1024 | 241 | self.main_window.swipe_to_viewfinder(self) | 285 | # Swipe to photo roll and back to viewfinder |
1025 | 286 | self.main_window.swipe_to_gallery(self) | ||
1026 | 287 | self.main_window.swipe_to_viewfinder(self) | ||
1027 | 242 | 288 | ||
1028 | 243 | def set_compression_quality(self, quality="Normal Quality"): | 289 | def set_compression_quality(self, quality="Normal Quality"): |
1029 | 244 | # open bottom edge | 290 | # open bottom edge |
1030 | @@ -276,9 +322,10 @@ | |||
1031 | 276 | # switch cameras and select the last resolution for the current camera | 322 | # switch cameras and select the last resolution for the current camera |
1032 | 277 | self.main_window.switch_cameras() | 323 | self.main_window.switch_cameras() |
1033 | 278 | resolutions = self.get_available_video_resolutions() | 324 | resolutions = self.get_available_video_resolutions() |
1037 | 279 | expected_resolution = resolutions[-1] | 325 | if len(resolutions) > 1: |
1038 | 280 | self.assertThat(expected_resolution, NotEquals(initial_resolution)) | 326 | expected_resolution = resolutions[-1] |
1039 | 281 | self.set_video_resolution(expected_resolution) | 327 | self.assertThat(expected_resolution, NotEquals(initial_resolution)) |
1040 | 328 | self.set_video_resolution(expected_resolution) | ||
1041 | 282 | 329 | ||
1042 | 283 | # switch back to the initial camera and record a video | 330 | # switch back to the initial camera and record a video |
1043 | 284 | self.main_window.switch_cameras() | 331 | self.main_window.switch_cameras() |
1044 | 285 | 332 | ||
1045 | === modified file 'tests/autopilot/camera_app/tests/test_diskspace.py' | |||
1046 | --- tests/autopilot/camera_app/tests/test_diskspace.py 2015-04-29 15:56:44 +0000 | |||
1047 | +++ tests/autopilot/camera_app/tests/test_diskspace.py 2016-03-23 15:42:41 +0000 | |||
1048 | @@ -61,9 +61,6 @@ | |||
1049 | 61 | # threshold as they all expect a normal situation at the start | 61 | # threshold as they all expect a normal situation at the start |
1050 | 62 | self.assertThat(self.diskSpaceAvailable(), GreaterThan(LOW_THRESHOLD)) | 62 | self.assertThat(self.diskSpaceAvailable(), GreaterThan(LOW_THRESHOLD)) |
1051 | 63 | 63 | ||
1052 | 64 | self.assertThat( | ||
1053 | 65 | self.main_window.get_qml_view().visible, Eventually(Equals(True))) | ||
1054 | 66 | |||
1055 | 67 | def tearDown(self): | 64 | def tearDown(self): |
1056 | 68 | super(TestCameraDiskSpace, self).tearDown() | 65 | super(TestCameraDiskSpace, self).tearDown() |
1057 | 69 | os.remove(self.diskFiller) if os.path.exists(self.diskFiller) else None | 66 | os.remove(self.diskFiller) if os.path.exists(self.diskFiller) else None |
1058 | @@ -75,8 +72,8 @@ | |||
1059 | 75 | exposure_button = self.main_window.get_exposure_button() | 72 | exposure_button = self.main_window.get_exposure_button() |
1060 | 76 | no_space_hint = self.main_window.get_no_space_hint() | 73 | no_space_hint = self.main_window.get_no_space_hint() |
1061 | 77 | 74 | ||
1064 | 78 | self.assertThat(exposure_button.enabled, Equals(True)) | 75 | self.assertThat(exposure_button.enabled, Eventually(Equals(True))) |
1065 | 79 | self.assertThat(no_space_hint.visible, Equals(False)) | 76 | self.assertThat(no_space_hint.visible, Eventually(Equals(False))) |
1066 | 80 | 77 | ||
1067 | 81 | self.setFreeSpaceTo(CRITICAL_THRESHOLD - MEGABYTE) | 78 | self.setFreeSpaceTo(CRITICAL_THRESHOLD - MEGABYTE) |
1068 | 82 | self.assertThat( | 79 | self.assertThat( |
1069 | @@ -89,8 +86,8 @@ | |||
1070 | 89 | self.assertThat( | 86 | self.assertThat( |
1071 | 90 | self.diskSpaceAvailable(), GreaterThan(CRITICAL_THRESHOLD)) | 87 | self.diskSpaceAvailable(), GreaterThan(CRITICAL_THRESHOLD)) |
1072 | 91 | 88 | ||
1075 | 92 | self.assertThat(exposure_button.enabled, Equals(True)) | 89 | self.assertThat(exposure_button.enabled, Eventually(Equals(True))) |
1076 | 93 | self.assertThat(no_space_hint.visible, Equals(False)) | 90 | self.assertThat(no_space_hint.visible, Eventually(Equals(False))) |
1077 | 94 | 91 | ||
1078 | 95 | def test_low_disk(self): | 92 | def test_low_disk(self): |
1079 | 96 | """Verify proper behavior when disk space becomes low""" | 93 | """Verify proper behavior when disk space becomes low""" |
1080 | 97 | 94 | ||
1081 | === modified file 'tests/autopilot/camera_app/tests/test_flash.py' | |||
1082 | --- tests/autopilot/camera_app/tests/test_flash.py 2015-07-03 13:27:48 +0000 | |||
1083 | +++ tests/autopilot/camera_app/tests/test_flash.py 2016-03-23 15:42:41 +0000 | |||
1084 | @@ -16,21 +16,14 @@ | |||
1085 | 16 | class TestCameraFlash(CameraAppTestCase): | 16 | class TestCameraFlash(CameraAppTestCase): |
1086 | 17 | """Tests the flash""" | 17 | """Tests the flash""" |
1087 | 18 | 18 | ||
1088 | 19 | """ This is needed to wait for the application to start. | ||
1089 | 20 | In the testfarm, the application may take some time to show up.""" | ||
1090 | 21 | def setUp(self): | ||
1091 | 22 | super(TestCameraFlash, self).setUp() | ||
1092 | 23 | self.assertThat( | ||
1093 | 24 | self.main_window.get_qml_view().visible, Eventually(Equals(True))) | ||
1094 | 25 | |||
1095 | 26 | def tearDown(self): | ||
1096 | 27 | super(TestCameraFlash, self).tearDown() | ||
1097 | 28 | |||
1098 | 29 | """Test that flash modes activate properly""" | 19 | """Test that flash modes activate properly""" |
1099 | 30 | def test_cycle_flash(self): | 20 | def test_cycle_flash(self): |
1100 | 31 | bottom_edge = self.main_window.get_bottom_edge() | 21 | bottom_edge = self.main_window.get_bottom_edge() |
1101 | 32 | bottom_edge.open() | 22 | bottom_edge.open() |
1102 | 33 | flash_button = self.main_window.get_flash_button() | 23 | flash_button = self.main_window.get_flash_button() |
1103 | 24 | if not flash_button: | ||
1104 | 25 | return | ||
1105 | 26 | |||
1106 | 34 | option_value_selector = self.main_window.get_option_value_selector() | 27 | option_value_selector = self.main_window.get_option_value_selector() |
1107 | 35 | 28 | ||
1108 | 36 | # open option value selector showing the possible values | 29 | # open option value selector showing the possible values |
1109 | @@ -67,10 +60,13 @@ | |||
1110 | 67 | bottom_edge = self.main_window.get_bottom_edge() | 60 | bottom_edge = self.main_window.get_bottom_edge() |
1111 | 68 | bottom_edge.open() | 61 | bottom_edge.open() |
1112 | 69 | flash_button = self.main_window.get_video_flash_button() | 62 | flash_button = self.main_window.get_video_flash_button() |
1113 | 63 | if not flash_button: | ||
1114 | 64 | return | ||
1115 | 65 | |||
1116 | 70 | option_value_selector = self.main_window.get_option_value_selector() | 66 | option_value_selector = self.main_window.get_option_value_selector() |
1117 | 71 | 67 | ||
1118 | 72 | # ensure initial state | 68 | # ensure initial state |
1120 | 73 | self.assertThat(flash_button.iconName, Equals("torch-off")) | 69 | self.assertThat(flash_button.iconName, Eventually(Equals("torch-off"))) |
1121 | 74 | 70 | ||
1122 | 75 | # open option value selector showing the possible values | 71 | # open option value selector showing the possible values |
1123 | 76 | self.pointing_device.move_to_object(flash_button) | 72 | self.pointing_device.move_to_object(flash_button) |
1124 | @@ -83,19 +79,22 @@ | |||
1125 | 83 | option = self.main_window.get_option_value_button("On") | 79 | option = self.main_window.get_option_value_button("On") |
1126 | 84 | self.pointing_device.move_to_object(option) | 80 | self.pointing_device.move_to_object(option) |
1127 | 85 | self.pointing_device.click() | 81 | self.pointing_device.click() |
1129 | 86 | self.assertThat(flash_button.iconName, Equals("torch-on")) | 82 | self.assertThat(flash_button.iconName, Eventually(Equals("torch-on"))) |
1130 | 87 | 83 | ||
1131 | 88 | # set flash to "off" | 84 | # set flash to "off" |
1132 | 89 | option = self.main_window.get_option_value_button("Off") | 85 | option = self.main_window.get_option_value_button("Off") |
1133 | 90 | self.pointing_device.move_to_object(option) | 86 | self.pointing_device.move_to_object(option) |
1134 | 91 | self.pointing_device.click() | 87 | self.pointing_device.click() |
1136 | 92 | self.assertThat(flash_button.iconName, Equals("torch-off")) | 88 | self.assertThat(flash_button.iconName, Eventually(Equals("torch-off"))) |
1137 | 93 | 89 | ||
1138 | 94 | """Test that flash and hdr modes are mutually exclusive""" | 90 | """Test that flash and hdr modes are mutually exclusive""" |
1139 | 95 | def test_flash_hdr_mutually_exclusive(self): | 91 | def test_flash_hdr_mutually_exclusive(self): |
1140 | 96 | bottom_edge = self.main_window.get_bottom_edge() | 92 | bottom_edge = self.main_window.get_bottom_edge() |
1141 | 97 | bottom_edge.open() | 93 | bottom_edge.open() |
1142 | 98 | flash_button = self.main_window.get_flash_button() | 94 | flash_button = self.main_window.get_flash_button() |
1143 | 95 | if not flash_button: | ||
1144 | 96 | return | ||
1145 | 97 | |||
1146 | 99 | hdr_button = self.main_window.get_hdr_button() | 98 | hdr_button = self.main_window.get_hdr_button() |
1147 | 100 | option_value_selector = self.main_window.get_option_value_selector() | 99 | option_value_selector = self.main_window.get_option_value_selector() |
1148 | 101 | 100 | ||
1149 | 102 | 101 | ||
1150 | === modified file 'tests/autopilot/camera_app/tests/test_focus.py' | |||
1151 | --- tests/autopilot/camera_app/tests/test_focus.py 2016-02-26 08:52:37 +0000 | |||
1152 | +++ tests/autopilot/camera_app/tests/test_focus.py 2016-03-23 15:42:41 +0000 | |||
1153 | @@ -18,26 +18,19 @@ | |||
1154 | 18 | class TestFocus(CameraAppTestCase): | 18 | class TestFocus(CameraAppTestCase): |
1155 | 19 | """Tests the focus""" | 19 | """Tests the focus""" |
1156 | 20 | 20 | ||
1157 | 21 | """ This is needed to wait for the application to start. | ||
1158 | 22 | In the testfarm, the application may take some time to show up.""" | ||
1159 | 23 | def setUp(self): | ||
1160 | 24 | super(TestFocus, self).setUp() | ||
1161 | 25 | self.assertThat( | ||
1162 | 26 | self.main_window.get_qml_view().visible, Eventually(Equals(True))) | ||
1163 | 27 | |||
1164 | 28 | def tearDown(self): | ||
1165 | 29 | super(TestFocus, self).tearDown() | ||
1166 | 30 | |||
1167 | 31 | def verify_focus_ring_after_click_at(self, ring, x, y): | 21 | def verify_focus_ring_after_click_at(self, ring, x, y): |
1168 | 32 | # The focus ring should be invisible in the beginning | 22 | # The focus ring should be invisible in the beginning |
1169 | 33 | self.assertThat(ring.opacity, Eventually(Equals(0.0))) | 23 | self.assertThat(ring.opacity, Eventually(Equals(0.0))) |
1170 | 34 | 24 | ||
1171 | 25 | focus_mouse_area = self.main_window.get_focus_mouse_area() | ||
1172 | 26 | self.assertThat(focus_mouse_area.enabled, Eventually(Equals(True))) | ||
1173 | 27 | |||
1174 | 35 | # Click in the designated spot | 28 | # Click in the designated spot |
1175 | 36 | self.pointing_device.move(x, y) | 29 | self.pointing_device.move(x, y) |
1176 | 37 | self.pointing_device.click() | 30 | self.pointing_device.click() |
1177 | 38 | 31 | ||
1178 | 39 | # The focus ring sould be visible now | 32 | # The focus ring sould be visible now |
1180 | 40 | self.assertThat(ring.opacity, Eventually(GreaterThan(0.5))) | 33 | self.assertThat(ring.opacity, Eventually(GreaterThan(0.1))) |
1181 | 41 | 34 | ||
1182 | 42 | # After some seconds the focus ring should fade out | 35 | # After some seconds the focus ring should fade out |
1183 | 43 | self.assertThat(ring.opacity, Eventually(Equals(0.0))) | 36 | self.assertThat(ring.opacity, Eventually(Equals(0.0))) |
1184 | @@ -45,19 +38,22 @@ | |||
1185 | 45 | """Test focusing in an area where we know the picture is""" | 38 | """Test focusing in an area where we know the picture is""" |
1186 | 46 | @unittest.skipIf(model() == 'Galaxy Nexus', 'Unusable with Mir on maguro') | 39 | @unittest.skipIf(model() == 'Galaxy Nexus', 'Unusable with Mir on maguro') |
1187 | 47 | def test_focus_valid_and_disappear(self): | 40 | def test_focus_valid_and_disappear(self): |
1188 | 41 | geometry = self.main_window.get_viewfinder_geometry() | ||
1189 | 48 | focus_ring = self.main_window.get_focus_ring() | 42 | focus_ring = self.main_window.get_focus_ring() |
1190 | 49 | feed = self.main_window.get_viewfinder_geometry() | ||
1191 | 50 | switch_cameras = self.main_window.get_swap_camera_button() | 43 | switch_cameras = self.main_window.get_swap_camera_button() |
1192 | 51 | exposure_button = self.main_window.get_exposure_button() | 44 | exposure_button = self.main_window.get_exposure_button() |
1193 | 52 | 45 | ||
1194 | 53 | # Click in the center of the viewfinder area | 46 | # Click in the center of the viewfinder area |
1196 | 54 | mid_x, mid_y = self.get_center(feed) | 47 | mid_x, mid_y = self.get_center(geometry) |
1197 | 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) |
1198 | 56 | 49 | ||
1199 | 57 | # Then try on the side edges and top edge to verify they | 50 | # Then try on the side edges and top edge to verify they |
1200 | 58 | # are focusable too | 51 | # are focusable too |
1203 | 59 | self.verify_focus_ring_after_click_at(focus_ring, 1, mid_y) | 52 | self.verify_focus_ring_after_click_at(focus_ring, |
1204 | 60 | self.verify_focus_ring_after_click_at(focus_ring, feed.width - 1, | 53 | geometry.globalRect.x + 1, mid_y) |
1205 | 54 | self.verify_focus_ring_after_click_at(focus_ring, | ||
1206 | 55 | geometry.globalRect.x + | ||
1207 | 56 | geometry.globalRect.width - 1, | ||
1208 | 61 | mid_y) | 57 | mid_y) |
1209 | 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) |
1210 | 63 | 59 | ||
1211 | @@ -69,10 +65,13 @@ | |||
1212 | 69 | # Click in the center of the viewfinder area | 65 | # Click in the center of the viewfinder area |
1213 | 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) |
1214 | 71 | 67 | ||
1216 | 72 | # Then try on the side edges and top edge to verify they | 68 | # Then try on the left, right and above the center to verify they |
1217 | 73 | # are focusable too | 69 | # are focusable too |
1220 | 74 | self.verify_focus_ring_after_click_at(focus_ring, 1, mid_y) | 70 | self.verify_focus_ring_after_click_at(focus_ring, |
1221 | 75 | self.verify_focus_ring_after_click_at(focus_ring, feed.width - 1, | 71 | geometry.globalRect.x + 1, mid_y) |
1222 | 72 | self.verify_focus_ring_after_click_at(focus_ring, | ||
1223 | 73 | geometry.globalRect.x + | ||
1224 | 74 | geometry.globalRect.width - 1, | ||
1225 | 76 | mid_y) | 75 | mid_y) |
1226 | 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) |
1227 | 78 | 77 | ||
1228 | 79 | 78 | ||
1229 | === modified file 'tests/autopilot/camera_app/tests/test_gallery_view.py' | |||
1230 | --- tests/autopilot/camera_app/tests/test_gallery_view.py 2015-04-29 15:56:44 +0000 | |||
1231 | +++ tests/autopilot/camera_app/tests/test_gallery_view.py 2016-03-23 15:42:41 +0000 | |||
1232 | @@ -7,7 +7,7 @@ | |||
1233 | 7 | 7 | ||
1234 | 8 | """Tests for the Camera App zoom""" | 8 | """Tests for the Camera App zoom""" |
1235 | 9 | 9 | ||
1237 | 10 | from testtools.matchers import Equals | 10 | from testtools.matchers import Equals, NotEquals |
1238 | 11 | from autopilot.matchers import Eventually | 11 | from autopilot.matchers import Eventually |
1239 | 12 | 12 | ||
1240 | 13 | from camera_app.tests import CameraAppTestCase | 13 | from camera_app.tests import CameraAppTestCase |
1241 | @@ -35,16 +35,19 @@ | |||
1242 | 35 | self.assertThat(slideshow_view.visible, Eventually(Equals(False))) | 35 | self.assertThat(slideshow_view.visible, Eventually(Equals(False))) |
1243 | 36 | self.assertThat(photogrid_view.visible, Eventually(Equals(True))) | 36 | self.assertThat(photogrid_view.visible, Eventually(Equals(True))) |
1244 | 37 | 37 | ||
1255 | 38 | def select_first_photo(self): | 38 | def select_media(self, index=0): |
1256 | 39 | # select the first photo | 39 | media = self.main_window.get_media(index) |
1257 | 40 | gallery = self.main_window.get_gallery() | 40 | checkbox = media.wait_select_single(objectName="mediaItemCheckBox") |
1258 | 41 | photo = gallery.wait_select_single(objectName="mediaItem0") | 41 | |
1259 | 42 | self.pointing_device.move_to_object(photo) | 42 | self.pointing_device.move_to_object(checkbox) |
1260 | 43 | 43 | ||
1261 | 44 | # do a long press to enter Multiselection mode | 44 | if checkbox.visible: |
1262 | 45 | self.pointing_device.press() | 45 | self.click() |
1263 | 46 | sleep(1) | 46 | else: |
1264 | 47 | self.pointing_device.release() | 47 | # do a long press to enter Multiselection mode |
1265 | 48 | self.pointing_device.press() | ||
1266 | 49 | sleep(1) | ||
1267 | 50 | self.pointing_device.release() | ||
1268 | 48 | 51 | ||
1269 | 49 | 52 | ||
1270 | 50 | class TestCameraGalleryView(CameraAppTestCase, TestCameraGalleryViewMixin): | 53 | class TestCameraGalleryView(CameraAppTestCase, TestCameraGalleryViewMixin): |
1271 | @@ -53,11 +56,6 @@ | |||
1272 | 53 | def setUp(self): | 56 | def setUp(self): |
1273 | 54 | self.delete_all_media() | 57 | self.delete_all_media() |
1274 | 55 | super(TestCameraGalleryView, self).setUp() | 58 | super(TestCameraGalleryView, self).setUp() |
1275 | 56 | self.assertThat( | ||
1276 | 57 | self.main_window.get_qml_view().visible, Eventually(Equals(True))) | ||
1277 | 58 | |||
1278 | 59 | def tearDown(self): | ||
1279 | 60 | super(TestCameraGalleryView, self).tearDown() | ||
1280 | 61 | 59 | ||
1281 | 62 | """Tests swiping to the gallery and pressing the back button""" | 60 | """Tests swiping to the gallery and pressing the back button""" |
1282 | 63 | def test_swipe_to_gallery(self): | 61 | def test_swipe_to_gallery(self): |
1283 | @@ -112,26 +110,55 @@ | |||
1284 | 112 | def setUp(self): | 110 | def setUp(self): |
1285 | 113 | self.delete_all_media() | 111 | self.delete_all_media() |
1286 | 114 | self.add_sample_video() | 112 | self.add_sample_video() |
1287 | 115 | |||
1288 | 116 | super(TestCameraGalleryViewWithVideo, self).setUp() | 113 | super(TestCameraGalleryViewWithVideo, self).setUp() |
1289 | 117 | self.assertThat( | ||
1290 | 118 | self.main_window.get_qml_view().visible, Eventually(Equals(True))) | ||
1291 | 119 | |||
1292 | 120 | def tearDown(self): | ||
1293 | 121 | super(TestCameraGalleryViewWithVideo, self).tearDown() | ||
1294 | 122 | 114 | ||
1295 | 123 | """Tests the thumnails for video load correctly in slideshow view""" | 115 | """Tests the thumnails for video load correctly in slideshow view""" |
1296 | 124 | def test_video_thumbnails(self): | 116 | def test_video_thumbnails(self): |
1297 | 125 | viewfinder = self.main_window.get_viewfinder() | 117 | viewfinder = self.main_window.get_viewfinder() |
1298 | 126 | gallery = self.main_window.get_gallery() | 118 | gallery = self.main_window.get_gallery() |
1307 | 127 | 119 | self.main_window.swipe_to_gallery(self) | |
1308 | 128 | self.main_window.swipe_to_gallery(self) | 120 | |
1309 | 129 | 121 | self.assertThat(viewfinder.inView, Eventually(Equals(False))) | |
1310 | 130 | self.assertThat(viewfinder.inView, Eventually(Equals(False))) | 122 | self.assertThat(gallery.inView, Eventually(Equals(True))) |
1311 | 131 | self.assertThat(gallery.inView, Eventually(Equals(True))) | 123 | |
1312 | 132 | 124 | spinner = gallery.wait_select_single("ActivityIndicator") | |
1313 | 133 | spinner = gallery.wait_select_single("ActivityIndicator") | 125 | self.assertThat(spinner.running, Eventually(Equals(False))) |
1314 | 134 | self.assertThat(spinner.running, Eventually(Equals(False))) | 126 | |
1315 | 127 | thumb_error = self.main_window.get_broken_media_icon() | ||
1316 | 128 | self.assertThat(thumb_error.opacity, Eventually(Equals(0.0))) | ||
1317 | 129 | |||
1318 | 130 | self.move_from_slideshow_to_photogrid() | ||
1319 | 131 | thumb_error = self.main_window.get_broken_media_icon() | ||
1320 | 132 | self.assertThat(thumb_error.opacity, Eventually(Equals(0.0))) | ||
1321 | 133 | |||
1322 | 134 | |||
1323 | 135 | class TestCameraGalleryViewWithBrokenVideo( | ||
1324 | 136 | TestCameraGalleryViewMixin, CameraAppTestCase): | ||
1325 | 137 | """Tests the camera gallery view with a broken video already present""" | ||
1326 | 138 | |||
1327 | 139 | def setUp(self): | ||
1328 | 140 | self.delete_all_media() | ||
1329 | 141 | self.add_sample_video(broken=True) | ||
1330 | 142 | super(TestCameraGalleryViewWithBrokenVideo, self).setUp() | ||
1331 | 143 | |||
1332 | 144 | """Tests the placeholder thumnails for broken video loads correctly""" | ||
1333 | 145 | def test_video_thumbnails(self): | ||
1334 | 146 | viewfinder = self.main_window.get_viewfinder() | ||
1335 | 147 | gallery = self.main_window.get_gallery() | ||
1336 | 148 | |||
1337 | 149 | self.main_window.swipe_to_gallery(self) | ||
1338 | 150 | self.assertThat(viewfinder.inView, Eventually(Equals(False))) | ||
1339 | 151 | self.assertThat(gallery.inView, Eventually(Equals(True))) | ||
1340 | 152 | |||
1341 | 153 | spinner = gallery.wait_select_single("ActivityIndicator") | ||
1342 | 154 | self.assertThat(spinner.running, Eventually(Equals(False))) | ||
1343 | 155 | |||
1344 | 156 | thumb_error = self.main_window.get_broken_media_icon() | ||
1345 | 157 | self.assertThat(thumb_error.opacity, Eventually(NotEquals(0.0))) | ||
1346 | 158 | |||
1347 | 159 | self.move_from_slideshow_to_photogrid() | ||
1348 | 160 | thumb_error = self.main_window.get_broken_media_icon() | ||
1349 | 161 | self.assertThat(thumb_error.opacity, Eventually(NotEquals(0.0))) | ||
1350 | 135 | 162 | ||
1351 | 136 | 163 | ||
1352 | 137 | class TestCameraGalleryViewWithPhoto( | 164 | class TestCameraGalleryViewWithPhoto( |
1353 | @@ -141,25 +168,17 @@ | |||
1354 | 141 | def setUp(self): | 168 | def setUp(self): |
1355 | 142 | self.delete_all_media() | 169 | self.delete_all_media() |
1356 | 143 | self.add_sample_photo() | 170 | self.add_sample_photo() |
1357 | 144 | |||
1358 | 145 | super(TestCameraGalleryViewWithPhoto, self).setUp() | 171 | super(TestCameraGalleryViewWithPhoto, self).setUp() |
1359 | 146 | self.assertThat( | ||
1360 | 147 | self.main_window.get_qml_view().visible, Eventually(Equals(True))) | ||
1361 | 148 | |||
1362 | 149 | def tearDown(self): | ||
1363 | 150 | super(TestCameraGalleryViewWithPhoto, self).tearDown() | ||
1364 | 151 | 172 | ||
1365 | 152 | """Test deleting photo from multiselection""" | 173 | """Test deleting photo from multiselection""" |
1366 | 153 | def test_delete_photo_from_multiselection(self): | 174 | def test_delete_photo_from_multiselection(self): |
1367 | 154 | self.main_window.swipe_to_gallery(self) | 175 | self.main_window.swipe_to_gallery(self) |
1368 | 155 | self.move_from_slideshow_to_photogrid() | 176 | self.move_from_slideshow_to_photogrid() |
1370 | 156 | self.select_first_photo() | 177 | self.select_media() |
1371 | 157 | 178 | ||
1372 | 158 | # open actions drawer | 179 | # open actions drawer |
1373 | 159 | gallery = self.main_window.get_gallery() | 180 | gallery = self.main_window.get_gallery() |
1377 | 160 | opt = gallery.wait_select_single(objectName="additionalActionsButton") | 181 | self.main_window.open_actions_drawer(gallery) |
1375 | 161 | self.pointing_device.move_to_object(opt) | ||
1376 | 162 | self.pointing_device.click() | ||
1378 | 163 | 182 | ||
1379 | 164 | # click delete action button | 183 | # click delete action button |
1380 | 165 | delete = gallery.wait_select_single(objectName="actionButtonDelete") | 184 | delete = gallery.wait_select_single(objectName="actionButtonDelete") |
1381 | @@ -178,7 +197,7 @@ | |||
1382 | 178 | def test_multiselection_mode(self): | 197 | def test_multiselection_mode(self): |
1383 | 179 | self.main_window.swipe_to_gallery(self) | 198 | self.main_window.swipe_to_gallery(self) |
1384 | 180 | self.move_from_slideshow_to_photogrid() | 199 | self.move_from_slideshow_to_photogrid() |
1386 | 181 | self.select_first_photo() | 200 | self.select_media() |
1387 | 182 | 201 | ||
1388 | 183 | # exit the multiselection mode | 202 | # exit the multiselection mode |
1389 | 184 | gallery = self.main_window.get_gallery() | 203 | gallery = self.main_window.get_gallery() |
1390 | @@ -191,3 +210,61 @@ | |||
1391 | 191 | 210 | ||
1392 | 192 | self.assertThat(slideshow_view.visible, Eventually(Equals(False))) | 211 | self.assertThat(slideshow_view.visible, Eventually(Equals(False))) |
1393 | 193 | self.assertThat(photogrid_view.visible, Eventually(Equals(True))) | 212 | self.assertThat(photogrid_view.visible, Eventually(Equals(True))) |
1394 | 213 | |||
1395 | 214 | |||
1396 | 215 | class TestCameraGalleryViewWithPhotosAndVideo( | ||
1397 | 216 | TestCameraGalleryViewMixin, CameraAppTestCase): | ||
1398 | 217 | """Tests the camera gallery view with two photos and a video""" | ||
1399 | 218 | |||
1400 | 219 | def setUp(self): | ||
1401 | 220 | self.delete_all_media() | ||
1402 | 221 | self.add_sample_photo() | ||
1403 | 222 | self.add_sample_video() | ||
1404 | 223 | super(TestCameraGalleryViewWithPhotosAndVideo, self).setUp() | ||
1405 | 224 | |||
1406 | 225 | def verify_share_state(self, expectedState, close=True): | ||
1407 | 226 | gallery = self.main_window.get_gallery() | ||
1408 | 227 | self.main_window.open_actions_drawer(gallery) | ||
1409 | 228 | |||
1410 | 229 | # verify expected state | ||
1411 | 230 | share = gallery.wait_select_single(objectName="actionButtonShare") | ||
1412 | 231 | self.assertThat(share.enabled, Eventually(Equals(expectedState))) | ||
1413 | 232 | |||
1414 | 233 | if (close): | ||
1415 | 234 | self.main_window.close_actions_drawer(gallery) | ||
1416 | 235 | else: | ||
1417 | 236 | return share | ||
1418 | 237 | |||
1419 | 238 | """Tests share button enable or disabled correctly in multiselection""" | ||
1420 | 239 | def test_multiselection_share_enabled(self): | ||
1421 | 240 | self.main_window.swipe_to_gallery(self) | ||
1422 | 241 | self.move_from_slideshow_to_photogrid() | ||
1423 | 242 | |||
1424 | 243 | # Verify options button disabled until we select something | ||
1425 | 244 | gallery = self.main_window.get_gallery() | ||
1426 | 245 | opt = gallery.wait_select_single(objectName="additionalActionsButton") | ||
1427 | 246 | self.assertThat(opt.visible, Eventually(Equals(False))) | ||
1428 | 247 | |||
1429 | 248 | # Verify that if we select one photo options and share are enabled | ||
1430 | 249 | self.select_media(0) | ||
1431 | 250 | self.assertThat(opt.visible, Eventually(Equals(True))) | ||
1432 | 251 | self.verify_share_state(True) | ||
1433 | 252 | |||
1434 | 253 | # Verify that it stays enabled with mixed media selected | ||
1435 | 254 | self.select_media(1) | ||
1436 | 255 | self.verify_share_state(True) | ||
1437 | 256 | |||
1438 | 257 | """Tests sharing with mixed media generates a warning dialog""" | ||
1439 | 258 | def test_no_share_mixed_media(self): | ||
1440 | 259 | self.main_window.swipe_to_gallery(self) | ||
1441 | 260 | self.move_from_slideshow_to_photogrid() | ||
1442 | 261 | |||
1443 | 262 | self.select_media(0) | ||
1444 | 263 | self.select_media(1) | ||
1445 | 264 | share = self.verify_share_state(True, close=False) | ||
1446 | 265 | |||
1447 | 266 | self.pointing_device.move_to_object(share) | ||
1448 | 267 | self.pointing_device.click() | ||
1449 | 268 | |||
1450 | 269 | gallery = self.main_window.get_gallery() | ||
1451 | 270 | gallery.wait_select_single(objectName="unableShareDialog") | ||
1452 | 194 | 271 | ||
1453 | === modified file 'tests/autopilot/camera_app/tests/test_options.py' | |||
1454 | --- tests/autopilot/camera_app/tests/test_options.py 2016-02-25 13:42:01 +0000 | |||
1455 | +++ tests/autopilot/camera_app/tests/test_options.py 2016-03-23 15:42:41 +0000 | |||
1456 | @@ -16,14 +16,6 @@ | |||
1457 | 16 | class TestCameraOptions(CameraAppTestCase): | 16 | class TestCameraOptions(CameraAppTestCase): |
1458 | 17 | """Tests the options overlay""" | 17 | """Tests the options overlay""" |
1459 | 18 | 18 | ||
1460 | 19 | """ This is needed to wait for the application to start. | ||
1461 | 20 | In the testfarm, the application may take some time to show up.""" | ||
1462 | 21 | def setUp(self): | ||
1463 | 22 | super(TestCameraOptions, self).setUp() | ||
1464 | 23 | # FIXME: this should be in parent class | ||
1465 | 24 | self.assertThat( | ||
1466 | 25 | self.main_window.get_qml_view().visible, Eventually(Equals(True))) | ||
1467 | 26 | |||
1468 | 27 | """Test that the options overlay closes properly by tapping""" | 19 | """Test that the options overlay closes properly by tapping""" |
1469 | 28 | def test_overlay_tap_to_close(self): | 20 | def test_overlay_tap_to_close(self): |
1470 | 29 | bottom_edge = self.main_window.get_bottom_edge() | 21 | bottom_edge = self.main_window.get_bottom_edge() |
1471 | 30 | 22 | ||
1472 | === modified file 'tests/autopilot/camera_app/tests/test_photo_editor.py' | |||
1473 | --- tests/autopilot/camera_app/tests/test_photo_editor.py 2015-07-07 11:09:05 +0000 | |||
1474 | +++ tests/autopilot/camera_app/tests/test_photo_editor.py 2016-03-23 15:42:41 +0000 | |||
1475 | @@ -20,13 +20,7 @@ | |||
1476 | 20 | def setUp(self): | 20 | def setUp(self): |
1477 | 21 | self.delete_all_media() | 21 | self.delete_all_media() |
1478 | 22 | self.add_sample_photo() | 22 | self.add_sample_photo() |
1479 | 23 | |||
1480 | 24 | super(TestCameraPhotoEditorWithPhoto, self).setUp() | 23 | super(TestCameraPhotoEditorWithPhoto, self).setUp() |
1481 | 25 | self.assertThat( | ||
1482 | 26 | self.main_window.get_qml_view().visible, Eventually(Equals(True))) | ||
1483 | 27 | |||
1484 | 28 | def tearDown(self): | ||
1485 | 29 | super(TestCameraPhotoEditorWithPhoto, self).tearDown() | ||
1486 | 30 | 24 | ||
1487 | 31 | """Tests editor opening and closing correctly for pictures""" | 25 | """Tests editor opening and closing correctly for pictures""" |
1488 | 32 | def test_editor_appears(self): | 26 | def test_editor_appears(self): |
1489 | @@ -37,11 +31,7 @@ | |||
1490 | 37 | self.main_window.swipe_to_gallery(self) | 31 | self.main_window.swipe_to_gallery(self) |
1491 | 38 | 32 | ||
1492 | 39 | self.assertThat(gallery.inView, Eventually(Equals(True))) | 33 | self.assertThat(gallery.inView, Eventually(Equals(True))) |
1498 | 40 | 34 | self.main_window.open_actions_drawer(gallery) | |
1494 | 41 | # open actions drawer | ||
1495 | 42 | opt = gallery.wait_select_single(objectName="additionalActionsButton") | ||
1496 | 43 | self.pointing_device.move_to_object(opt) | ||
1497 | 44 | self.pointing_device.click() | ||
1499 | 45 | 35 | ||
1500 | 46 | # If the editor button is not there when in the gallery view, then | 36 | # If the editor button is not there when in the gallery view, then |
1501 | 47 | # we are not on a system that has the UI extras package installed or | 37 | # we are not on a system that has the UI extras package installed or |
1502 | @@ -77,13 +67,7 @@ | |||
1503 | 77 | def setUp(self): | 67 | def setUp(self): |
1504 | 78 | self.delete_all_media() | 68 | self.delete_all_media() |
1505 | 79 | self.add_sample_video() | 69 | self.add_sample_video() |
1506 | 80 | |||
1507 | 81 | super(TestCameraPhotoEditorWithVideo, self).setUp() | 70 | super(TestCameraPhotoEditorWithVideo, self).setUp() |
1508 | 82 | self.assertThat( | ||
1509 | 83 | self.main_window.get_qml_view().visible, Eventually(Equals(True))) | ||
1510 | 84 | |||
1511 | 85 | def tearDown(self): | ||
1512 | 86 | super(TestCameraPhotoEditorWithVideo, self).tearDown() | ||
1513 | 87 | 71 | ||
1514 | 88 | """Tests editor not being available for videos""" | 72 | """Tests editor not being available for videos""" |
1515 | 89 | def test_editor_not_on_videos(self): | 73 | def test_editor_not_on_videos(self): |
1516 | @@ -95,11 +79,7 @@ | |||
1517 | 95 | self.main_window.swipe_to_gallery(self) | 79 | self.main_window.swipe_to_gallery(self) |
1518 | 96 | 80 | ||
1519 | 97 | self.assertThat(gallery.inView, Eventually(Equals(True))) | 81 | self.assertThat(gallery.inView, Eventually(Equals(True))) |
1525 | 98 | 82 | self.main_window.open_actions_drawer(gallery) | |
1521 | 99 | # open actions drawer | ||
1522 | 100 | opt = gallery.wait_select_single(objectName="additionalActionsButton") | ||
1523 | 101 | self.pointing_device.move_to_object(opt) | ||
1524 | 102 | self.pointing_device.click() | ||
1526 | 103 | 83 | ||
1527 | 104 | # If the editor button is not there when in the gallery view, then | 84 | # If the editor button is not there when in the gallery view, then |
1528 | 105 | # we are not on a system that has the UI extras package installed or | 85 | # we are not on a system that has the UI extras package installed or |
1529 | 106 | 86 | ||
1530 | === modified file 'tests/autopilot/camera_app/tests/test_zoom.py' | |||
1531 | --- tests/autopilot/camera_app/tests/test_zoom.py 2016-01-11 17:07:21 +0000 | |||
1532 | +++ tests/autopilot/camera_app/tests/test_zoom.py 2016-03-23 15:42:41 +0000 | |||
1533 | @@ -19,16 +19,6 @@ | |||
1534 | 19 | class TestCameraZoom(CameraAppTestCase): | 19 | class TestCameraZoom(CameraAppTestCase): |
1535 | 20 | """Tests the main camera features""" | 20 | """Tests the main camera features""" |
1536 | 21 | 21 | ||
1537 | 22 | """ This is needed to wait for the application to start. | ||
1538 | 23 | In the testfarm, the application may take some time to show up.""" | ||
1539 | 24 | def setUp(self): | ||
1540 | 25 | super(TestCameraZoom, self).setUp() | ||
1541 | 26 | self.assertThat( | ||
1542 | 27 | self.main_window.get_qml_view().visible, Eventually(Equals(True))) | ||
1543 | 28 | |||
1544 | 29 | def tearDown(self): | ||
1545 | 30 | super(TestCameraZoom, self).tearDown() | ||
1546 | 31 | |||
1547 | 32 | def activate_zoom(self): | 22 | def activate_zoom(self): |
1548 | 33 | viewfinder = self.main_window.get_viewfinder_geometry() | 23 | viewfinder = self.main_window.get_viewfinder_geometry() |
1549 | 34 | viewfinder_center = self.get_center(viewfinder) | 24 | viewfinder_center = self.get_center(viewfinder) |
1550 | 35 | 25 | ||
1551 | === modified file 'tests/unittests/CMakeLists.txt' | |||
1552 | --- tests/unittests/CMakeLists.txt 2016-02-26 15:24:03 +0000 | |||
1553 | +++ tests/unittests/CMakeLists.txt 2016-03-23 15:42:41 +0000 | |||
1554 | @@ -7,7 +7,7 @@ | |||
1555 | 7 | add_executable(tst_QmlTests tst_QmlTests.cpp) | 7 | add_executable(tst_QmlTests tst_QmlTests.cpp) |
1556 | 8 | qt5_use_modules(tst_QmlTests Core Qml Quick Test QuickTest) | 8 | qt5_use_modules(tst_QmlTests Core Qml Quick Test QuickTest) |
1557 | 9 | target_link_libraries(tst_QmlTests ${TPL_QT5_LIBRARIES}) | 9 | target_link_libraries(tst_QmlTests ${TPL_QT5_LIBRARIES}) |
1559 | 10 | add_test(tst_QmlTests ${XVFB_RUN_CMD} ${CMAKE_CURRENT_BINARY_DIR}/tst_QmlTests -import ${CMAKE_SOURCE_DIR}) | 10 | add_test(tst_QmlTests ${XVFB_RUN_CMD} ${CMAKE_CURRENT_BINARY_DIR}/tst_QmlTests -import ${CMAKE_SOURCE_DIR} -import ${CMAKE_BINARY_DIR}) |
1560 | 11 | 11 | ||
1561 | 12 | # copy qml test files to build dir | 12 | # copy qml test files to build dir |
1562 | 13 | file(GLOB qmlTestFiles RELATIVE ${CMAKE_SOURCE_DIR}/tests/unittests/ *qml) | 13 | file(GLOB qmlTestFiles RELATIVE ${CMAKE_SOURCE_DIR}/tests/unittests/ *qml) |
1563 | 14 | 14 | ||
1564 | === added file 'tests/unittests/tst_PhotogridView.qml' | |||
1565 | --- tests/unittests/tst_PhotogridView.qml 1970-01-01 00:00:00 +0000 | |||
1566 | +++ tests/unittests/tst_PhotogridView.qml 2016-03-23 15:42:41 +0000 | |||
1567 | @@ -0,0 +1,88 @@ | |||
1568 | 1 | /* | ||
1569 | 2 | * Copyright 2016 Canonical Ltd. | ||
1570 | 3 | * | ||
1571 | 4 | * This program is free software; you can redistribute it and/or modify | ||
1572 | 5 | * it under the terms of the GNU General Public License as published by | ||
1573 | 6 | * the Free Software Foundation; version 3. | ||
1574 | 7 | * | ||
1575 | 8 | * This program is distributed in the hope that it will be useful, | ||
1576 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1577 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1578 | 11 | * GNU General Public License for more details. | ||
1579 | 12 | * | ||
1580 | 13 | * You should have received a copy of the GNU General Public License | ||
1581 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1582 | 15 | * | ||
1583 | 16 | */ | ||
1584 | 17 | |||
1585 | 18 | import QtQuick 2.4 | ||
1586 | 19 | import QtTest 1.0 | ||
1587 | 20 | import "../../" | ||
1588 | 21 | import "../../.." //Needed for out of source build | ||
1589 | 22 | |||
1590 | 23 | TestCase { | ||
1591 | 24 | name: "PhotogridView" | ||
1592 | 25 | |||
1593 | 26 | function test_mixedMediaSelection_data() { | ||
1594 | 27 | return [ | ||
1595 | 28 | { // one item only | ||
1596 | 29 | isMixedMedia: false, | ||
1597 | 30 | listItems: [ | ||
1598 | 31 | { fileType: "image", selected: true, fileURL: "" } | ||
1599 | 32 | ] | ||
1600 | 33 | }, | ||
1601 | 34 | { // mixed media but only non-mixed selected | ||
1602 | 35 | isMixedMedia: false, | ||
1603 | 36 | listItems: [ | ||
1604 | 37 | { fileType: "video", selected: false, fileURL: "" }, | ||
1605 | 38 | { fileType: "image", selected: true, fileURL: "" }, | ||
1606 | 39 | { fileType: "image", selected: true, fileURL: "" } | ||
1607 | 40 | ] | ||
1608 | 41 | }, | ||
1609 | 42 | { // mixed media | ||
1610 | 43 | isMixedMedia: true, | ||
1611 | 44 | listItems: [ | ||
1612 | 45 | { fileType: "video", selected: true, fileURL: "" }, | ||
1613 | 46 | { fileType: "image", selected: true, fileURL: "" }, | ||
1614 | 47 | { fileType: "image", selected: true, fileURL: "" } | ||
1615 | 48 | ] | ||
1616 | 49 | }, | ||
1617 | 50 | ]; | ||
1618 | 51 | } | ||
1619 | 52 | |||
1620 | 53 | function test_mixedMediaSelection(data) { | ||
1621 | 54 | list.clear() | ||
1622 | 55 | list.data = data.listItems; | ||
1623 | 56 | for (var i = 0; i < data.listItems.length; i++) { | ||
1624 | 57 | list.append(data.listItems[i]); | ||
1625 | 58 | } | ||
1626 | 59 | list.updateSelectedFiles(); | ||
1627 | 60 | grid.model = list | ||
1628 | 61 | compare(grid.selectionContainsMixedMedia(), data.isMixedMedia, "Mixed media not detected correctly") | ||
1629 | 62 | } | ||
1630 | 63 | |||
1631 | 64 | ListModel { | ||
1632 | 65 | id: list | ||
1633 | 66 | property var data | ||
1634 | 67 | property var selectedFiles: [] | ||
1635 | 68 | function updateSelectedFiles() { | ||
1636 | 69 | // need to re-assign entire list due to the way list properties work in QML | ||
1637 | 70 | var selected = []; | ||
1638 | 71 | for (var i = 0; i < list.count; i++) { | ||
1639 | 72 | if (list.data[i].selected) selected.push(i); | ||
1640 | 73 | } | ||
1641 | 74 | list.selectedFiles = selected; | ||
1642 | 75 | } | ||
1643 | 76 | function get(i, key) { | ||
1644 | 77 | return list.data[i][key]; | ||
1645 | 78 | } | ||
1646 | 79 | } | ||
1647 | 80 | |||
1648 | 81 | PhotogridView { | ||
1649 | 82 | id: grid | ||
1650 | 83 | width: 600 | ||
1651 | 84 | height: 800 | ||
1652 | 85 | inView: true | ||
1653 | 86 | inSelectionMode: true | ||
1654 | 87 | } | ||
1655 | 88 | } |