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

Proposed by Florian Boucault
Status: Merged
Approved by: Florian Boucault
Approved revision: 658
Merged at revision: 636
Proposed branch: lp:camera-app/staging
Merge into: lp:camera-app
Diff against target: 2752 lines (+657/-1424)
31 files modified
BottomEdgeIndicators.qml (+1/-1)
CameraApp/CMakeLists.txt (+2/-4)
CameraApp/advancedcamerasettings.cpp (+3/-3)
CameraApp/components.cpp (+9/-0)
CameraApp/qstorageinfo.cpp (+0/-378)
CameraApp/qstorageinfo.h (+0/-102)
CameraApp/qstorageinfo_p.h (+0/-84)
CameraApp/qstorageinfo_unix.cpp (+0/-442)
CameraApp/storagelocations.cpp (+121/-0)
CameraApp/storagelocations.h (+50/-0)
CameraApp/storagemonitor.cpp (+34/-1)
CameraApp/storagemonitor.h (+5/-0)
GalleryView.qml (+3/-3)
GalleryViewHeader.qml (+3/-3)
PictureReview.qml (+46/-0)
ProcessingFeedback.qml (+43/-0)
SharePopover.qml (+1/-9)
Snapshot.qml (+0/-98)
ViewFinderExportConfirmation.qml (+40/-21)
ViewFinderOverlay.qml (+103/-40)
ViewFinderOverlayLoader.qml (+4/-1)
ViewFinderView.qml (+112/-65)
cameraapplication.cpp (+0/-106)
cameraapplication.h (+0/-15)
debian/rules (+0/-4)
manifest.json.in (+1/-1)
run_tests.sh (+0/-13)
tests/autopilot/camera_app/tests/test_focus.py (+32/-28)
tests/autopilot/camera_app/tests/test_options.py (+27/-0)
tests/unittests/CMakeLists.txt (+1/-0)
tests/unittests/qstorageinfo_stub.cpp (+16/-2)
To merge this branch: bzr merge lp:camera-app/staging
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Needs Fixing
Ubuntu Phablet Team Pending
Review via email: mp+284427@code.launchpad.net

Commit message

New release:
- Allow tap to focus up to the left/right edge of the screen
- Query the manual focus support to enable/disable tap-to-focus as the backend supports it
- Do not crash if no QCameraExposureControl is available (true with Unity 7)
- Before setting a photo capture resolution, always checks it is included in the possible resolution options
- Fix bottom edge mouse handling: ensure that clicks on the bottom edge open it and that any click outside of option buttons dismiss the bottom edge
- Various performance improvements to minimize the time between shots and between pressing the shutter button and the actual capture
- When the GPS fix is accurate to less than 100m display the GPS icon in red and don't save location
- Display error messages when disk write fails and when SD card is not writeable when switching to it
- Disable record/shuttle button while PhotoRoll hint is visible

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:camera-app/staging updated
659. By Florian Boucault

Add flake8 tests to unit tests

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'BottomEdgeIndicators.qml'
2--- BottomEdgeIndicators.qml 2015-11-17 15:25:03 +0000
3+++ BottomEdgeIndicators.qml 2016-02-26 17:17:00 +0000
4@@ -83,7 +83,7 @@
5 Icon {
6 id: indicatorIcon
7 anchors.fill: parent
8- color: "white"
9+ color: modelData.colorize ? "red" : "white"
10 name: modelData && modelData.isToggle ? modelData.icon : (modelData.get(model.selectedIndex) ? modelData.get(model.selectedIndex).icon : "")
11 source: name ? "image://theme/%1".arg(name) : (modelData.iconSource || "")
12 visible: source != ""
13
14=== modified file 'CameraApp/CMakeLists.txt'
15--- CameraApp/CMakeLists.txt 2015-11-18 06:44:35 +0000
16+++ CameraApp/CMakeLists.txt 2016-02-26 17:17:00 +0000
17@@ -7,8 +7,7 @@
18 fileoperations.cpp
19 foldersmodel.cpp
20 storagemonitor.cpp
21- qstorageinfo.cpp
22- qstorageinfo_unix.cpp
23+ storagelocations.cpp
24 )
25
26 set(plugin_HDRS
27@@ -17,8 +16,7 @@
28 fileoperations.h
29 foldersmodel.h
30 storagemonitor.h
31- qstorageinfo.h
32- qstorageinfo_p.h
33+ storagelocations.h
34 )
35
36 add_library(camera-qml SHARED ${plugin_SRCS} ${plugin_HDRS})
37
38=== modified file 'CameraApp/advancedcamerasettings.cpp'
39--- CameraApp/advancedcamerasettings.cpp 2016-01-05 13:08:28 +0000
40+++ CameraApp/advancedcamerasettings.cpp 2016-02-26 17:17:00 +0000
41@@ -237,11 +237,11 @@
42
43 m_cameraFlashControl = flashControlFromCamera(m_camera);
44 m_cameraExposureControl = exposureControlFromCamera(m_camera);
45- QVariant exposureMode = m_hdrEnabled ? QVariant::fromValue(ExposureHdr)
46- : QVariant::fromValue(QCameraExposure::ExposureAuto);
47- m_cameraExposureControl->setValue(QCameraExposureControl::ExposureMode, exposureMode);
48
49 if (m_cameraExposureControl) {
50+ QVariant exposureMode = m_hdrEnabled ? QVariant::fromValue(ExposureHdr)
51+ : QVariant::fromValue(QCameraExposure::ExposureAuto);
52+ m_cameraExposureControl->setValue(QCameraExposureControl::ExposureMode, exposureMode);
53 QObject::connect(m_cameraExposureControl,
54 SIGNAL(actualValueChanged(int)),
55 this, SLOT(onExposureValueChanged(int)));
56
57=== modified file 'CameraApp/components.cpp'
58--- CameraApp/components.cpp 2015-01-22 16:15:08 +0000
59+++ CameraApp/components.cpp 2016-02-26 17:17:00 +0000
60@@ -24,6 +24,14 @@
61 #include "fileoperations.h"
62 #include "foldersmodel.h"
63 #include "storagemonitor.h"
64+#include "storagelocations.h"
65+
66+static QObject* StorageLocations_singleton_factory(QQmlEngine* engine, QJSEngine* scriptEngine)
67+{
68+ Q_UNUSED(engine);
69+ Q_UNUSED(scriptEngine);
70+ return new StorageLocations();
71+}
72
73 void Components::registerTypes(const char *uri)
74 {
75@@ -34,6 +42,7 @@
76 qmlRegisterType<FileOperations>(uri, 0, 1, "FileOperations");
77 qmlRegisterType<FoldersModel>(uri, 0, 1, "FoldersModel");
78 qmlRegisterType<StorageMonitor>(uri, 0, 1, "StorageMonitor");
79+ qmlRegisterSingletonType<StorageLocations>(uri, 0, 1, "StorageLocations", StorageLocations_singleton_factory);
80 }
81
82 void Components::initializeEngine(QQmlEngine *engine, const char *uri)
83
84=== removed file 'CameraApp/qstorageinfo.cpp'
85--- CameraApp/qstorageinfo.cpp 2015-01-27 14:34:31 +0000
86+++ CameraApp/qstorageinfo.cpp 1970-01-01 00:00:00 +0000
87@@ -1,378 +0,0 @@
88-/****************************************************************************
89-**
90-** Copyright (C) 2014 Ivan Komissarov <ABBAPOH@gmail.com>
91-** Contact: http://www.qt-project.org/legal
92-**
93-** This file is part of the QtCore module of the Qt Toolkit.
94-**
95-** This program is free software; you can redistribute it and/or modify
96-** it under the terms of the GNU General Public License as published by
97-** the Free Software Foundation; version 3.
98-**
99-** This program is distributed in the hope that it will be useful,
100-** but WITHOUT ANY WARRANTY; without even the implied warranty of
101-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
102-** GNU General Public License for more details.
103-**
104-** You should have received a copy of the GNU General Public License
105-** along with this program. If not, see <http://www.gnu.org/licenses/>.
106-**
107-****************************************************************************/
108-
109-#include "qstorageinfo.h"
110-#include "qstorageinfo_p.h"
111-
112-QT_BEGIN_NAMESPACE
113-
114-/*!
115- \class QStorageInfo
116- \inmodule QtCore
117- \since 5.4
118- \brief Provides information about currently mounted storage and drives.
119-
120- \ingroup io
121- \ingroup shared
122-
123- Allows retrieving information about the volume's space, its mount point,
124- label, and filesystem name.
125-
126- You can create an instance of QStorageInfo by passing the path to the
127- volume's mount point as a constructor parameter, or you can set it using
128- the setPath() method. The static mountedVolumes() method can be used to get the
129- list of all mounted filesystems.
130-
131- QStorageInfo always caches the retrieved information, but you can call
132- refresh() to invalidate the cache.
133-
134- The following example retrieves the most common information about the root
135- volume of the system, and prints information about it.
136-
137- \snippet code/src_corelib_io_qstorageinfo.cpp 2
138-*/
139-
140-/*!
141- Constructs an empty QStorageInfo object.
142-
143- Objects created with the default constructor will be invalid and therefore
144- not ready for use.
145-
146- \sa setPath(), isReady(), isValid()
147-*/
148-QStorageInfo::QStorageInfo()
149- : d(new QStorageInfoPrivate)
150-{
151-}
152-
153-/*!
154- Constructs a new QStorageInfo object that gives information about the volume
155- mounted at \a path.
156-
157- If you pass a directory or file, the QStorageInfo object will refer to the
158- volume where this directory or file is located.
159- You can check if the created object is correct using the isValid() method.
160-
161- The following example shows how to get the volume on which the application is
162- located. It is recommended to always check that the volume is ready and valid.
163-
164- \snippet code/src_corelib_io_qstorageinfo.cpp 0
165-
166- \sa setPath()
167-*/
168-QStorageInfo::QStorageInfo(const QString &path)
169- : d(new QStorageInfoPrivate)
170-{
171- setPath(path);
172-}
173-
174-/*!
175- Constructs a new QStorageInfo object that gives information about the volume
176- containing the \a dir folder.
177-*/
178-QStorageInfo::QStorageInfo(const QDir &dir)
179- : d(new QStorageInfoPrivate)
180-{
181- setPath(dir.absolutePath());
182-}
183-
184-/*!
185- Constructs a new QStorageInfo object that is a copy of the \a other QStorageInfo object.
186-*/
187-QStorageInfo::QStorageInfo(const QStorageInfo &other)
188- : d(other.d)
189-{
190-}
191-
192-/*!
193- Destroys the QStorageInfo object and frees its resources.
194-*/
195-QStorageInfo::~QStorageInfo()
196-{
197-}
198-
199-/*!
200- Makes a copy of the QStorageInfo object \a other and assigns it to this QStorageInfo object.
201-*/
202-QStorageInfo &QStorageInfo::operator=(const QStorageInfo &other)
203-{
204- d = other.d;
205- return *this;
206-}
207-
208-/*!
209- \fn QStorageInfo &QStorageInfo::operator=(QStorageInfo &&other)
210-
211- Assigns \a other to this QStorageInfo instance.
212-*/
213-
214-/*!
215- \fn void QStorageInfo::swap(QStorageInfo &other)
216-
217- Swaps this volume info with \a other. This function is very fast and
218- never fails.
219-*/
220-
221-/*!
222- Sets this QStorageInfo object to the filesystem mounted where \a path is located.
223-
224- \a path can either be a root path of the filesystem, a directory, or a file
225- within that filesystem.
226-
227- \sa rootPath()
228-*/
229-void QStorageInfo::setPath(const QString &path)
230-{
231- if (d->rootPath == path)
232- return;
233- d.detach();
234- d->rootPath = path;
235- d->doStat();
236-}
237-
238-/*!
239- Returns the mount point of the filesystem this QStorageInfo object
240- represents.
241-
242- On Windows, it returns the volume letter in case the volume is not mounted to
243- a directory.
244-
245- Note that the value returned by rootPath() is the real mount point of a
246- volume, and may not be equal to the value passed to the constructor or setPath()
247- method. For example, if you have only the root volume in the system, and
248- pass '/directory' to setPath(), then this method will return '/'.
249-
250- \sa setPath(), device()
251-*/
252-QString QStorageInfo::rootPath() const
253-{
254- return d->rootPath;
255-}
256-
257-/*!
258- Returns the size (in bytes) available for the current user. It returns
259- the total size available if the user is the root user or a system administrator.
260-
261- This size can be less than or equal to the free size returned by
262- bytesFree() function.
263-
264- \sa bytesTotal(), bytesFree()
265-*/
266-qint64 QStorageInfo::bytesAvailable() const
267-{
268- return d->bytesAvailable;
269-}
270-
271-/*!
272- Returns the number of free bytes in a volume. Note that if there are
273- quotas on the filesystem, this value can be larger than the value
274- returned by bytesAvailable().
275-
276- \sa bytesTotal(), bytesAvailable()
277-*/
278-qint64 QStorageInfo::bytesFree() const
279-{
280- return d->bytesFree;
281-}
282-
283-/*!
284- Returns the total volume size in bytes.
285-
286- \sa bytesFree(), bytesAvailable()
287-*/
288-qint64 QStorageInfo::bytesTotal() const
289-{
290- return d->bytesTotal;
291-}
292-
293-/*!
294- Returns the type name of the filesystem.
295-
296- This is a platform-dependent function, and filesystem names can vary
297- between different operating systems. For example, on Windows filesystems
298- they can be named \c NTFS, and on Linux they can be named \c ntfs-3g or \c fuseblk.
299-
300- \sa name()
301-*/
302-QByteArray QStorageInfo::fileSystemType() const
303-{
304- return d->fileSystemType;
305-}
306-
307-/*!
308- Returns the device for this volume.
309-
310- For example, on Unix filesystems (including OS X), this returns the
311- devpath like \c /dev/sda0 for local storages. On Windows, it returns the UNC
312- path starting with \c \\\\?\\ for local storages (in other words, the volume GUID).
313-
314- \sa rootPath()
315-*/
316-QByteArray QStorageInfo::device() const
317-{
318- return d->device;
319-}
320-
321-/*!
322- Returns the human-readable name of a filesystem, usually called \c label.
323-
324- Not all filesystems support this feature. In this case, the value returned by
325- this method could be empty. An empty string is returned if the file system
326- does not support labels, or if no label is set.
327-
328- On Linux, retrieving the volume's label requires \c udev to be present in the
329- system.
330-
331- \sa fileSystemType()
332-*/
333-QString QStorageInfo::name() const
334-{
335- return d->name;
336-}
337-
338-/*!
339- Returns the volume's name, if available, or the root path if not.
340-*/
341-QString QStorageInfo::displayName() const
342-{
343- if (!d->name.isEmpty())
344- return d->name;
345- return d->rootPath;
346-}
347-
348-/*!
349- \fn bool QStorageInfo::isRoot() const
350-
351- Returns true if this QStorageInfo represents the system root volume; false
352- otherwise.
353-
354- On Unix filesystems, the root volume is a volume mounted on \c /. On Windows,
355- the root volume is the volume where the OS is installed.
356-
357- \sa root()
358-*/
359-
360-/*!
361- Returns true if the current filesystem is protected from writing; false
362- otherwise.
363-*/
364-bool QStorageInfo::isReadOnly() const
365-{
366- return d->readOnly;
367-}
368-
369-/*!
370- Returns true if the current filesystem is ready to work; false otherwise. For
371- example, false is returned if the CD volume is not inserted.
372-
373- Note that fileSystemType(), name(), bytesTotal(), bytesFree(), and
374- bytesAvailable() will return invalid data until the volume is ready.
375-
376- \sa isValid()
377-*/
378-bool QStorageInfo::isReady() const
379-{
380- return d->ready;
381-}
382-
383-/*!
384- Returns true if the QStorageInfo specified by rootPath exists and is mounted
385- correctly.
386-
387- \sa isReady()
388-*/
389-bool QStorageInfo::isValid() const
390-{
391- return d->valid;
392-}
393-
394-/*!
395- Resets QStorageInfo's internal cache.
396-
397- QStorageInfo caches information about storage to speed up performance.
398- QStorageInfo retrieves information during object construction and/or when calling
399- the setPath() method. You have to manually reset the cache by calling this
400- function to update storage information.
401-*/
402-void QStorageInfo::refresh()
403-{
404- d.detach();
405- d->doStat();
406-}
407-
408-/*!
409- Returns the list of QStorageInfo objects that corresponds to the list of currently
410- mounted filesystems.
411-
412- On Windows, this returns the drives visible in the \gui{My Computer} folder. On Unix
413- operating systems, it returns the list of all mounted filesystems (except for
414- pseudo filesystems).
415-
416- Returns all currently mounted filesystems by default.
417-
418- The example shows how to retrieve all available filesystems, skipping read-only ones.
419-
420- \snippet code/src_corelib_io_qstorageinfo.cpp 1
421-
422- \sa root()
423-*/
424-QList<QStorageInfo> QStorageInfo::mountedVolumes()
425-{
426- return QStorageInfoPrivate::mountedVolumes();
427-}
428-
429-Q_GLOBAL_STATIC_WITH_ARGS(QStorageInfo, getRoot, (QStorageInfoPrivate::root()))
430-
431-/*!
432- Returns a QStorageInfo object that represents the system root volume.
433-
434- On Unix systems this call returns the root ('/') volume; in Windows the volume where
435- the operating system is installed.
436-
437- \sa isRoot()
438-*/
439-QStorageInfo QStorageInfo::root()
440-{
441- return *getRoot();
442-}
443-
444-/*!
445- \fn inline bool operator==(const QStorageInfo &first, const QStorageInfo &second)
446-
447- \relates QStorageInfo
448-
449- Returns true if the \a first QStorageInfo object refers to the same drive or volume
450- as the \a second; otherwise it returns false.
451-
452- Note that the result of comparing two invalid QStorageInfo objects is always
453- positive.
454-*/
455-
456-/*!
457- \fn inline bool operator!=(const QStorageInfo &first, const QStorageInfo &second)
458-
459- \relates QStorageInfo
460-
461- Returns true if the \a first QStorageInfo object refers to a different drive or
462- volume than the \a second; otherwise returns false.
463-*/
464-
465-QT_END_NAMESPACE
466
467=== removed file 'CameraApp/qstorageinfo.h'
468--- CameraApp/qstorageinfo.h 2015-01-27 14:34:31 +0000
469+++ CameraApp/qstorageinfo.h 1970-01-01 00:00:00 +0000
470@@ -1,102 +0,0 @@
471-/****************************************************************************
472-**
473-** Copyright (C) 2014 Ivan Komissarov <ABBAPOH@gmail.com>
474-** Contact: http://www.qt-project.org/legal
475-**
476-** This file is part of the QtCore module of the Qt Toolkit.
477-**
478-** This program is free software; you can redistribute it and/or modify
479-** it under the terms of the GNU General Public License as published by
480-** the Free Software Foundation; version 3.
481-**
482-** This program is distributed in the hope that it will be useful,
483-** but WITHOUT ANY WARRANTY; without even the implied warranty of
484-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
485-** GNU General Public License for more details.
486-**
487-** You should have received a copy of the GNU General Public License
488-** along with this program. If not, see <http://www.gnu.org/licenses/>.
489-**
490-****************************************************************************/
491-
492-#ifndef QSTORAGEINFO_H
493-#define QSTORAGEINFO_H
494-
495-#include <QtCore/qbytearray.h>
496-#include <QtCore/qdir.h>
497-#include <QtCore/qlist.h>
498-#include <QtCore/qmetatype.h>
499-#include <QtCore/qstring.h>
500-#include <QtCore/qshareddata.h>
501-
502-QT_BEGIN_NAMESPACE
503-
504-class QStorageInfoPrivate;
505-class Q_CORE_EXPORT QStorageInfo
506-{
507-public:
508- QStorageInfo();
509- explicit QStorageInfo(const QString &path);
510- explicit QStorageInfo(const QDir &dir);
511- QStorageInfo(const QStorageInfo &other);
512- ~QStorageInfo();
513-
514- QStorageInfo &operator=(const QStorageInfo &other);
515-#ifdef Q_COMPILER_RVALUE_REFS
516- inline QStorageInfo &operator=(QStorageInfo &&other)
517- { qSwap(d, other.d); return *this; }
518-#endif
519-
520- inline void swap(QStorageInfo &other)
521- { qSwap(d, other.d); }
522-
523- void setPath(const QString &path);
524-
525- QString rootPath() const;
526- QByteArray device() const;
527- QByteArray fileSystemType() const;
528- QString name() const;
529- QString displayName() const;
530-
531- qint64 bytesTotal() const;
532- qint64 bytesFree() const;
533- qint64 bytesAvailable() const;
534-
535- inline bool isRoot() const;
536- bool isReadOnly() const;
537- bool isReady() const;
538- bool isValid() const;
539-
540- void refresh();
541-
542- static QList<QStorageInfo> mountedVolumes();
543- static QStorageInfo root();
544-
545-private:
546- friend class QStorageInfoPrivate;
547- friend bool operator==(const QStorageInfo &first, const QStorageInfo &second);
548- QExplicitlySharedDataPointer<QStorageInfoPrivate> d;
549-};
550-
551-inline bool operator==(const QStorageInfo &first, const QStorageInfo &second)
552-{
553- if (first.d == second.d)
554- return true;
555- return first.device() == second.device();
556-}
557-
558-inline bool operator!=(const QStorageInfo &first, const QStorageInfo &second)
559-{
560- return !(first == second);
561-}
562-
563-inline bool QStorageInfo::isRoot() const
564-{ return *this == QStorageInfo::root(); }
565-
566-Q_DECLARE_SHARED(QStorageInfo)
567-
568-QT_END_NAMESPACE
569-
570-Q_DECLARE_METATYPE(QStorageInfo)
571-
572-#endif // QSTORAGEINFO_H
573
574=== removed file 'CameraApp/qstorageinfo_p.h'
575--- CameraApp/qstorageinfo_p.h 2015-01-27 14:34:31 +0000
576+++ CameraApp/qstorageinfo_p.h 1970-01-01 00:00:00 +0000
577@@ -1,84 +0,0 @@
578-/****************************************************************************
579-**
580-** Copyright (C) 2014 Ivan Komissarov <ABBAPOH@gmail.com>
581-** Contact: http://www.qt-project.org/legal
582-**
583-**
584-** This file is part of the QtCore module of the Qt Toolkit.
585-**
586-** This program is free software; you can redistribute it and/or modify
587-** it under the terms of the GNU General Public License as published by
588-** the Free Software Foundation; version 3.
589-**
590-** This program is distributed in the hope that it will be useful,
591-** but WITHOUT ANY WARRANTY; without even the implied warranty of
592-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
593-** GNU General Public License for more details.
594-**
595-** You should have received a copy of the GNU General Public License
596-** along with this program. If not, see <http://www.gnu.org/licenses/>.
597-**
598-****************************************************************************/
599-
600-#ifndef QSTORAGEINFO_P_H
601-#define QSTORAGEINFO_P_H
602-
603-//
604-// W A R N I N G
605-// -------------
606-//
607-// This file is not part of the Qt API. It exists purely as an
608-// implementation detail. This header file may change from version to
609-// version without notice, or even be removed.
610-//
611-// We mean it.
612-//
613-
614-#include "qstorageinfo.h"
615-
616-QT_BEGIN_NAMESPACE
617-
618-class QStorageInfoPrivate : public QSharedData
619-{
620-public:
621- inline QStorageInfoPrivate() : QSharedData(),
622- bytesTotal(0), bytesFree(0), bytesAvailable(0),
623- readOnly(false), ready(false), valid(false)
624- {}
625-
626- void initRootPath();
627- void doStat();
628-
629- static QList<QStorageInfo> mountedVolumes();
630- static QStorageInfo root();
631-
632-protected:
633-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
634- void retreiveVolumeInfo();
635- void retreiveDiskFreeSpace();
636-#elif defined(Q_OS_MAC)
637- void retrievePosixInfo();
638- void retrieveUrlProperties(bool initRootPath = false);
639- void retrieveLabel();
640-#elif defined(Q_OS_UNIX)
641- void retreiveVolumeInfo();
642-#endif
643-
644-public:
645- QString rootPath;
646- QByteArray device;
647- QByteArray fileSystemType;
648- QString name;
649-
650- qint64 bytesTotal;
651- qint64 bytesFree;
652- qint64 bytesAvailable;
653-
654- bool readOnly;
655- bool ready;
656- bool valid;
657-};
658-
659-QT_END_NAMESPACE
660-
661-#endif // QSTORAGEINFO_P_H
662
663=== removed file 'CameraApp/qstorageinfo_unix.cpp'
664--- CameraApp/qstorageinfo_unix.cpp 2015-01-27 14:34:31 +0000
665+++ CameraApp/qstorageinfo_unix.cpp 1970-01-01 00:00:00 +0000
666@@ -1,442 +0,0 @@
667-/****************************************************************************
668-**
669-** Copyright (C) 2014 Ivan Komissarov <ABBAPOH@gmail.com>
670-** Contact: http://www.qt-project.org/legal
671-**
672-** This file is part of the QtCore module of the Qt Toolkit.
673-**
674-** This program is free software; you can redistribute it and/or modify
675-** it under the terms of the GNU General Public License as published by
676-** the Free Software Foundation; version 3.
677-**
678-** This program is distributed in the hope that it will be useful,
679-** but WITHOUT ANY WARRANTY; without even the implied warranty of
680-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
681-** GNU General Public License for more details.
682-**
683-** You should have received a copy of the GNU General Public License
684-** along with this program. If not, see <http://www.gnu.org/licenses/>.
685-**
686-****************************************************************************/
687-
688-#include "qstorageinfo_p.h"
689-
690-#include <QtCore/qdiriterator.h>
691-#include <QtCore/qfileinfo.h>
692-#include <QtCore/qtextstream.h>
693-
694-// Copy this definition directly instead of importing the whole file which
695-// would pull in more dependencies we don't need.
696-//#include <QtCore/private/qcore_unix_p.h>
697-#define EINTR_LOOP(var, cmd) \
698- do { \
699- var = cmd; \
700- } while (var == -1 && errno == EINTR)
701-
702-#include <errno.h>
703-#include <sys/stat.h>
704-
705-#if defined(Q_OS_BSD4)
706-# include <sys/mount.h>
707-# include <sys/statvfs.h>
708-#elif defined(Q_OS_ANDROID)
709-# include <sys/mount.h>
710-# include <sys/vfs.h>
711-# include <mntent.h>
712-#elif defined(Q_OS_LINUX)
713-# include <mntent.h>
714-# include <sys/statvfs.h>
715-#elif defined(Q_OS_SOLARIS)
716-# include <sys/mnttab.h>
717-#else
718-# include <sys/statvfs.h>
719-#endif
720-
721-#if defined(Q_OS_BSD4)
722-# define QT_STATFSBUF struct statvfs
723-# define QT_STATFS ::statvfs
724-#elif defined(Q_OS_ANDROID)
725-# define QT_STATFS ::statfs
726-# define QT_STATFSBUF struct statfs
727-# if !defined(ST_RDONLY)
728-# define ST_RDONLY 1 // hack for missing define on Android
729-# endif
730-#else
731-# if defined(QT_LARGEFILE_SUPPORT)
732-# define QT_STATFSBUF struct statvfs64
733-# define QT_STATFS ::statvfs64
734-# else
735-# define QT_STATFSBUF struct statvfs
736-# define QT_STATFS ::statvfs
737-# endif // QT_LARGEFILE_SUPPORT
738-#endif // Q_OS_BSD4
739-
740-QT_BEGIN_NAMESPACE
741-
742-static bool isPseudoFs(const QString &mountDir, const QByteArray &type)
743-{
744- if (mountDir.startsWith(QLatin1String("/dev"))
745- || mountDir.startsWith(QLatin1String("/proc"))
746- || mountDir.startsWith(QLatin1String("/sys"))
747- || mountDir.startsWith(QLatin1String("/var/run"))
748- || mountDir.startsWith(QLatin1String("/var/lock"))) {
749- return true;
750- }
751- if (type == "tmpfs")
752- return true;
753-#if defined(Q_OS_LINUX)
754- if (type == "rootfs" || type == "rpc_pipefs")
755- return true;
756-#endif
757-
758- return false;
759-}
760-
761-class QStorageIterator
762-{
763-public:
764- QStorageIterator();
765- ~QStorageIterator();
766-
767- inline bool isValid() const;
768- inline bool next();
769- inline QString rootPath() const;
770- inline QByteArray fileSystemType() const;
771- inline QByteArray device() const;
772-private:
773-#if defined(Q_OS_BSD4)
774- struct statfs *stat_buf;
775- int entryCount;
776- int currentIndex;
777-#elif defined(Q_OS_SOLARIS)
778- FILE *fp;
779- mnttab mnt;
780-#elif defined(Q_OS_ANDROID)
781- QFile file;
782- QByteArray m_rootPath;
783- QByteArray m_fileSystemType;
784- QByteArray m_device;
785-#elif defined(Q_OS_LINUX)
786- FILE *fp;
787- mntent mnt;
788- QByteArray buffer;
789-#endif
790-};
791-
792-#if defined(Q_OS_BSD4)
793-
794-inline QStorageIterator::QStorageIterator()
795- : entryCount(::getmntinfo(&stat_buf, 0)),
796- currentIndex(-1)
797-{
798-}
799-
800-inline QStorageIterator::~QStorageIterator()
801-{
802-}
803-
804-inline bool QStorageIterator::isValid() const
805-{
806- return entryCount != -1;
807-}
808-
809-inline bool QStorageIterator::next()
810-{
811- return ++currentIndex < entryCount;
812-}
813-
814-inline QString QStorageIterator::rootPath() const
815-{
816- return QFile::decodeName(stat_buf[currentIndex].f_mntonname);
817-}
818-
819-inline QByteArray QStorageIterator::fileSystemType() const
820-{
821- return QByteArray(stat_buf[currentIndex].f_fstypename);
822-}
823-
824-inline QByteArray QStorageIterator::device() const
825-{
826- return QByteArray(stat_buf[currentIndex].f_mntfromname);
827-}
828-
829-#elif defined(Q_OS_SOLARIS)
830-
831-static const char pathMounted[] = "/etc/mnttab";
832-
833-inline QStorageIterator::QStorageIterator()
834-{
835- const int fd = qt_safe_open(pathMounted, O_RDONLY);
836- fp = ::fdopen(fd, "r");
837-}
838-
839-inline QStorageIterator::~QStorageIterator()
840-{
841- if (fp)
842- ::fclose(fp);
843-}
844-
845-inline bool QStorageIterator::isValid() const
846-{
847- return fp != Q_NULLPTR;
848-}
849-
850-inline bool QStorageIterator::next()
851-{
852- return ::getmntent(fp, &mnt) == Q_NULLPTR;
853-}
854-
855-inline QString QStorageIterator::rootPath() const
856-{
857- return QFile::decodeName(mnt->mnt_mountp);
858-}
859-
860-inline QByteArray QStorageIterator::fileSystemType() const
861-{
862- return QByteArray(mnt->mnt_fstype);
863-}
864-
865-inline QByteArray QStorageIterator::device() const
866-{
867- return QByteArray(mnt->mnt_mntopts);
868-}
869-
870-#elif defined(Q_OS_ANDROID)
871-
872-static const char pathMounted[] = "/proc/mounts";
873-
874-inline QStorageIterator::QStorageIterator()
875-{
876- file.setFileName(pathMounted);
877- file.open(QIODevice::ReadOnly | QIODevice::Text);
878-}
879-
880-inline QStorageIterator::~QStorageIterator()
881-{
882-}
883-
884-inline bool QStorageIterator::isValid() const
885-{
886- return file.isOpen();
887-}
888-
889-inline bool QStorageIterator::next()
890-{
891- QList<QByteArray> data;
892- do {
893- const QByteArray line = file.readLine();
894- data = line.split(' ');
895- } while (data.count() < 3 && !file.atEnd());
896-
897- if (file.atEnd())
898- return false;
899- m_device = data.at(0);
900- m_rootPath = data.at(1);
901- m_fileSystemType = data.at(2);
902-
903- return true;
904-}
905-
906-inline QString QStorageIterator::rootPath() const
907-{
908- return QFile::decodeName(m_rootPath);
909-}
910-
911-inline QByteArray QStorageIterator::fileSystemType() const
912-{
913- return m_fileSystemType;
914-}
915-
916-inline QByteArray QStorageIterator::device() const
917-{
918- return m_device;
919-}
920-
921-#elif defined(Q_OS_LINUX)
922-
923-static const char pathMounted[] = "/etc/mtab";
924-static const int bufferSize = 3*PATH_MAX; // 2 paths (mount point+device) and metainfo
925-
926-inline QStorageIterator::QStorageIterator() :
927- buffer(QByteArray(bufferSize, 0))
928-{
929- fp = ::setmntent(pathMounted, "r");
930-}
931-
932-inline QStorageIterator::~QStorageIterator()
933-{
934- if (fp)
935- ::endmntent(fp);
936-}
937-
938-inline bool QStorageIterator::isValid() const
939-{
940- return fp != Q_NULLPTR;
941-}
942-
943-inline bool QStorageIterator::next()
944-{
945- return ::getmntent_r(fp, &mnt, buffer.data(), buffer.size()) != Q_NULLPTR;
946-}
947-
948-inline QString QStorageIterator::rootPath() const
949-{
950- return QFile::decodeName(mnt.mnt_dir);
951-}
952-
953-inline QByteArray QStorageIterator::fileSystemType() const
954-{
955- return QByteArray(mnt.mnt_type);
956-}
957-
958-inline QByteArray QStorageIterator::device() const
959-{
960- return QByteArray(mnt.mnt_fsname);
961-}
962-
963-#else
964-
965-inline QStorageIterator::QStorageIterator()
966-{
967-}
968-
969-inline QStorageIterator::~QStorageIterator()
970-{
971-}
972-
973-inline bool QStorageIterator::isValid() const
974-{
975- return false;
976-}
977-
978-inline bool QStorageIterator::next()
979-{
980- return false;
981-}
982-
983-inline QString QStorageIterator::rootPath() const
984-{
985- return QString();
986-}
987-
988-inline QByteArray QStorageIterator::fileSystemType() const
989-{
990- return QByteArray();
991-}
992-
993-inline QByteArray QStorageIterator::device() const
994-{
995- return QByteArray();
996-}
997-
998-#endif
999-
1000-void QStorageInfoPrivate::initRootPath()
1001-{
1002- rootPath = QFileInfo(rootPath).canonicalFilePath();
1003-
1004- if (rootPath.isEmpty())
1005- return;
1006-
1007- QStorageIterator it;
1008- if (!it.isValid()) {
1009- rootPath = QStringLiteral("/");
1010- return;
1011- }
1012-
1013- int maxLength = 0;
1014- const QString oldRootPath = rootPath;
1015- rootPath.clear();
1016-
1017- while (it.next()) {
1018- const QString mountDir = it.rootPath();
1019- const QByteArray fsName = it.fileSystemType();
1020- if (isPseudoFs(mountDir, fsName))
1021- continue;
1022- // we try to find most suitable entry
1023- if (oldRootPath.startsWith(mountDir) && maxLength < mountDir.length()) {
1024- maxLength = mountDir.length();
1025- rootPath = mountDir;
1026- device = it.device();
1027- fileSystemType = fsName;
1028- }
1029- }
1030-}
1031-
1032-static inline QString retrieveLabel(const QByteArray &device)
1033-{
1034-#ifdef Q_OS_LINUX
1035- static const char pathDiskByLabel[] = "/dev/disk/by-label";
1036-
1037- QDirIterator it(QLatin1String(pathDiskByLabel), QDir::NoDotAndDotDot);
1038- while (it.hasNext()) {
1039- it.next();
1040- QFileInfo fileInfo(it.fileInfo());
1041- if (fileInfo.isSymLink() && fileInfo.symLinkTarget().toLocal8Bit() == device)
1042- return fileInfo.fileName();
1043- }
1044-#else
1045- Q_UNUSED(device);
1046-#endif
1047-
1048- return QString();
1049-}
1050-
1051-void QStorageInfoPrivate::doStat()
1052-{
1053- initRootPath();
1054- if (rootPath.isEmpty())
1055- return;
1056-
1057- retreiveVolumeInfo();
1058- name = retrieveLabel(device);
1059-}
1060-
1061-void QStorageInfoPrivate::retreiveVolumeInfo()
1062-{
1063- QT_STATFSBUF statfs_buf;
1064- int result;
1065- EINTR_LOOP(result, QT_STATFS(QFile::encodeName(rootPath).constData(), &statfs_buf));
1066- if (result == 0) {
1067- valid = true;
1068- ready = true;
1069-
1070- bytesTotal = statfs_buf.f_blocks * statfs_buf.f_bsize;
1071- bytesFree = statfs_buf.f_bfree * statfs_buf.f_bsize;
1072- bytesAvailable = statfs_buf.f_bavail * statfs_buf.f_bsize;
1073-#if defined(Q_OS_ANDROID)
1074-#if defined(_STATFS_F_FLAGS)
1075- readOnly = (statfs_buf.f_flags & ST_RDONLY) != 0;
1076-#endif
1077-#else
1078- readOnly = (statfs_buf.f_flag & ST_RDONLY) != 0;
1079-#endif
1080- }
1081-}
1082-
1083-QList<QStorageInfo> QStorageInfoPrivate::mountedVolumes()
1084-{
1085- QStorageIterator it;
1086- if (!it.isValid())
1087- return QList<QStorageInfo>() << root();
1088-
1089- QList<QStorageInfo> volumes;
1090-
1091- while (it.next()) {
1092- const QString mountDir = it.rootPath();
1093- const QByteArray fsName = it.fileSystemType();
1094- if (isPseudoFs(mountDir, fsName))
1095- continue;
1096-
1097- volumes.append(QStorageInfo(mountDir));
1098- }
1099-
1100- return volumes;
1101-}
1102-
1103-QStorageInfo QStorageInfoPrivate::root()
1104-{
1105- return QStorageInfo(QStringLiteral("/"));
1106-}
1107-
1108-QT_END_NAMESPACE
1109
1110=== added file 'CameraApp/storagelocations.cpp'
1111--- CameraApp/storagelocations.cpp 1970-01-01 00:00:00 +0000
1112+++ CameraApp/storagelocations.cpp 2016-02-26 17:17:00 +0000
1113@@ -0,0 +1,121 @@
1114+/*
1115+ * Copyright (C) 2016 Canonical, Ltd.
1116+ *
1117+ * This program is free software; you can redistribute it and/or modify
1118+ * it under the terms of the GNU General Public License as published by
1119+ * the Free Software Foundation; version 3.
1120+ *
1121+ * This program is distributed in the hope that it will be useful,
1122+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1123+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1124+ * GNU General Public License for more details.
1125+ *
1126+ * You should have received a copy of the GNU General Public License
1127+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1128+ */
1129+
1130+#include "storagelocations.h"
1131+
1132+#include <QCoreApplication>
1133+#include <QStandardPaths>
1134+#include <QStorageInfo>
1135+
1136+StorageLocations::StorageLocations(QObject *parent) : QObject(parent)
1137+{
1138+}
1139+
1140+QString StorageLocations::picturesLocation() const
1141+{
1142+ QStringList locations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation);
1143+ if (locations.isEmpty()) {
1144+ return QString();
1145+ }
1146+ QString location = locations.at(0) + "/" + QCoreApplication::applicationName();
1147+ QDir dir;
1148+ // Transition from old directory 'camera' to new one; see bug #1363112
1149+ // https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1363112
1150+ dir.rename(locations.at(0) + "/" + "camera", location);
1151+ dir.mkpath(location);
1152+ return location;
1153+}
1154+
1155+QString StorageLocations::videosLocation() const
1156+{
1157+ QStringList locations = QStandardPaths::standardLocations(QStandardPaths::MoviesLocation);
1158+ if (locations.isEmpty()) {
1159+ return QString();
1160+ }
1161+ QString location = locations.at(0) + "/" + QCoreApplication::applicationName();
1162+ QDir dir;
1163+ // Transition from old directory 'camera' to new one; see bug #1363112
1164+ // https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1363112
1165+ dir.rename(locations.at(0) + "/" + "camera", location);
1166+ dir.mkpath(location);
1167+ return location;
1168+}
1169+
1170+QString StorageLocations::temporaryLocation() const
1171+{
1172+ QStringList locations = QStandardPaths::standardLocations(QStandardPaths::TempLocation);
1173+ if (locations.isEmpty()) {
1174+ return QString();
1175+ }
1176+ QString location = locations.at(0);
1177+ QDir dir;
1178+ dir.mkpath(location);
1179+ return location;
1180+}
1181+
1182+QString StorageLocations::removableStorageLocation() const
1183+{
1184+ QString mediaRoot("/media/" + qgetenv("USER"));
1185+ Q_FOREACH(QStorageInfo volume, QStorageInfo::mountedVolumes()) {
1186+ if (volume.rootPath().startsWith(mediaRoot) &&
1187+ volume.isValid() && volume.isReady()) {
1188+ return volume.rootPath();
1189+ }
1190+ }
1191+
1192+ return QString();
1193+}
1194+
1195+QString StorageLocations::removableStoragePicturesLocation() const
1196+{
1197+ QString storageLocation = removableStorageLocation();
1198+ if (storageLocation.isEmpty()) {
1199+ return QString();
1200+ }
1201+
1202+ QStringList locations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation);
1203+ QString pictureDir = QString(locations.at(0)).split("/").value(3);
1204+ if (pictureDir.isEmpty()){
1205+ return QString();
1206+ }
1207+ QString location = storageLocation + "/" + pictureDir + "/" + QCoreApplication::applicationName();
1208+ QDir dir;
1209+ dir.mkpath(location);
1210+ return location;
1211+}
1212+
1213+QString StorageLocations::removableStorageVideosLocation() const
1214+{
1215+ QString storageLocation = removableStorageLocation();
1216+ if (storageLocation.isEmpty()) {
1217+ return QString();
1218+ }
1219+
1220+ QStringList locations = QStandardPaths::standardLocations(QStandardPaths::MoviesLocation);
1221+ QString movieDir = QString(locations.at(0)).split("/").value(3);
1222+ if (movieDir.isEmpty()){
1223+ return QString();
1224+ }
1225+ QString location = storageLocation + "/" + movieDir + "/" + QCoreApplication::applicationName();
1226+ QDir dir;
1227+ dir.mkpath(location);
1228+ return location;
1229+}
1230+
1231+bool StorageLocations::removableStoragePresent() const
1232+{
1233+ return !removableStorageLocation().isEmpty();
1234+}
1235
1236=== added file 'CameraApp/storagelocations.h'
1237--- CameraApp/storagelocations.h 1970-01-01 00:00:00 +0000
1238+++ CameraApp/storagelocations.h 2016-02-26 17:17:00 +0000
1239@@ -0,0 +1,50 @@
1240+/*
1241+ * Copyright (C) 2016 Canonical, Ltd.
1242+ *
1243+ * This program is free software; you can redistribute it and/or modify
1244+ * it under the terms of the GNU General Public License as published by
1245+ * the Free Software Foundation; version 3.
1246+ *
1247+ * This program is distributed in the hope that it will be useful,
1248+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1249+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1250+ * GNU General Public License for more details.
1251+ *
1252+ * You should have received a copy of the GNU General Public License
1253+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1254+ */
1255+
1256+#ifndef STORAGELOCATIONS_H
1257+#define STORAGELOCATIONS_H
1258+
1259+#include <QObject>
1260+
1261+class StorageLocations : public QObject
1262+{
1263+ Q_OBJECT
1264+
1265+ Q_PROPERTY(QString picturesLocation READ picturesLocation CONSTANT)
1266+ Q_PROPERTY(QString videosLocation READ videosLocation CONSTANT)
1267+ Q_PROPERTY(QString temporaryLocation READ temporaryLocation CONSTANT)
1268+ Q_PROPERTY(QString removableStorageLocation READ removableStorageLocation CONSTANT)
1269+ Q_PROPERTY(QString removableStoragePicturesLocation READ removableStoragePicturesLocation CONSTANT)
1270+ Q_PROPERTY(QString removableStorageVideosLocation READ removableStorageVideosLocation CONSTANT)
1271+ Q_PROPERTY(bool removableStoragePresent READ removableStoragePresent NOTIFY removableStoragePresentChanged)
1272+
1273+public:
1274+ explicit StorageLocations(QObject *parent = 0);
1275+
1276+ QString picturesLocation() const;
1277+ QString videosLocation() const;
1278+ QString temporaryLocation() const;
1279+ QString removableStorageLocation() const;
1280+ QString removableStoragePicturesLocation() const;
1281+ QString removableStorageVideosLocation() const;
1282+
1283+ bool removableStoragePresent() const;
1284+
1285+Q_SIGNALS:
1286+ void removableStoragePresentChanged();
1287+};
1288+
1289+#endif // STORAGELOCATIONS_H
1290
1291=== modified file 'CameraApp/storagemonitor.cpp'
1292--- CameraApp/storagemonitor.cpp 2015-01-23 19:28:44 +0000
1293+++ CameraApp/storagemonitor.cpp 2016-02-26 17:17:00 +0000
1294@@ -15,9 +15,10 @@
1295 */
1296
1297 #include "storagemonitor.h"
1298+#include "storagelocations.h"
1299
1300 StorageMonitor::StorageMonitor(QObject *parent) :
1301- QObject(parent), m_low(false), m_criticallyLow(false)
1302+ QObject(parent), m_low(false), m_criticallyLow(false), m_writeable(true)
1303 {
1304 m_timer.setInterval(POLL_INTERVAL);
1305 m_timer.setSingleShot(false);
1306@@ -54,6 +55,32 @@
1307 }
1308 }
1309
1310+void StorageMonitor::checkWriteable()
1311+{
1312+ bool writeable = true;
1313+
1314+ QString mediaRoot("/media/" + qgetenv("USER"));
1315+ if (m_storage.rootPath().startsWith(mediaRoot)) {
1316+ // check only for external media, assume internal media is always writeable
1317+ if (m_storage.isReadOnly()) {
1318+ writeable = false;
1319+ } else {
1320+ StorageLocations locations;
1321+ QDir storageRoot(locations.removableStoragePicturesLocation());
1322+ QFile testFile(storageRoot.absoluteFilePath(".write_test"));
1323+ bool opened = testFile.open(QIODevice::WriteOnly | QIODevice::Unbuffered);
1324+ if (!opened || testFile.write("x", 1) != 1) writeable = false;
1325+ testFile.close();
1326+ testFile.remove();
1327+ }
1328+ }
1329+
1330+ if (m_writeable != writeable) {
1331+ m_writeable = writeable;
1332+ Q_EMIT isWriteableChanged();
1333+ }
1334+}
1335+
1336 void StorageMonitor::setLocation(QString location)
1337 {
1338 if (location != m_location) {
1339@@ -62,6 +89,7 @@
1340
1341 m_storage.setPath(m_location);
1342 checkDiskSpace();
1343+ checkWriteable();
1344 if (m_storage.isValid()) {
1345 m_timer.start();
1346 }
1347@@ -84,3 +112,8 @@
1348 {
1349 return m_criticallyLow;
1350 }
1351+
1352+bool StorageMonitor::isWriteable() const
1353+{
1354+ return m_writeable;
1355+}
1356
1357=== modified file 'CameraApp/storagemonitor.h'
1358--- CameraApp/storagemonitor.h 2015-01-23 19:29:03 +0000
1359+++ CameraApp/storagemonitor.h 2016-02-26 17:17:00 +0000
1360@@ -34,6 +34,7 @@
1361 Q_PROPERTY(QString location READ location WRITE setLocation NOTIFY locationChanged)
1362 Q_PROPERTY(bool diskSpaceLow READ diskSpaceLow NOTIFY diskSpaceLowChanged)
1363 Q_PROPERTY(bool diskSpaceCriticallyLow READ diskSpaceCriticallyLow NOTIFY diskSpaceCriticallyLowChanged)
1364+ Q_PROPERTY(bool isWriteable READ isWriteable NOTIFY isWriteableChanged)
1365
1366 public:
1367 explicit StorageMonitor(QObject *parent = 0);
1368@@ -42,21 +43,25 @@
1369 void setLocation(QString location);
1370 bool diskSpaceLow() const;
1371 bool diskSpaceCriticallyLow() const;
1372+ bool isWriteable() const;
1373
1374 Q_SIGNALS:
1375 void locationChanged();
1376 void diskSpaceLowChanged();
1377 void diskSpaceCriticallyLowChanged();
1378+ void isWriteableChanged();
1379
1380 private Q_SLOTS:
1381 void refresh();
1382
1383 private:
1384 void checkDiskSpace();
1385+ void checkWriteable();
1386
1387 private:
1388 bool m_low;
1389 bool m_criticallyLow;
1390+ bool m_writeable;
1391 QTimer m_timer;
1392 QString m_location;
1393 QStorageInfo m_storage;
1394
1395=== modified file 'GalleryView.qml'
1396--- GalleryView.qml 2015-11-26 11:29:18 +0000
1397+++ GalleryView.qml 2016-02-26 17:17:00 +0000
1398@@ -30,9 +30,9 @@
1399 property bool userSelectionMode: false
1400 property Item currentView: state == "GRID" ? photogridView : slideshowView
1401 property var model: FoldersModel {
1402- folders: [application.picturesLocation, application.videosLocation,
1403- application.removableStoragePicturesLocation,
1404- application.removableStorageVideosLocation]
1405+ folders: [StorageLocations.picturesLocation, StorageLocations.videosLocation,
1406+ StorageLocations.removableStoragePicturesLocation,
1407+ StorageLocations.removableStorageVideosLocation]
1408 typeFilters: !main.contentExportMode ? [ "image", "video" ]
1409 : [MimeTypeMapper.contentTypeToMimeType(main.transferContentType)]
1410 singleSelectionOnly: main.transfer.selectionType === ContentTransfer.Single
1411
1412=== modified file 'GalleryViewHeader.qml'
1413--- GalleryViewHeader.qml 2015-10-30 15:01:10 +0000
1414+++ GalleryViewHeader.qml 2016-02-26 17:17:00 +0000
1415@@ -73,7 +73,7 @@
1416 }
1417 width: units.gu(8)
1418 iconName: "back"
1419- iconColor: theme.palette.normal.foregroundText
1420+ iconColor: "white"
1421 onClicked: editMode ? header.exitEditor() : header.exit()
1422 }
1423
1424@@ -81,7 +81,7 @@
1425 text: main.contentExportMode || userSelectionMode ? i18n.tr("Select") :
1426 (editMode ? i18n.tr("Edit Photo") : i18n.tr("Photo Roll"))
1427 fontSize: "x-large"
1428- color: theme.palette.normal.foregroundText
1429+ color: "white"
1430 elide: Text.ElideRight
1431 Layout.fillWidth: true
1432 }
1433@@ -228,7 +228,7 @@
1434 }
1435 text: model.text
1436 elide: Text.ElideRight
1437- color: action.enabled ? theme.palette.normal.foregroundText : Qt.darker(theme.palette.normal.foregroundText, 2.0)
1438+ color: action.enabled ? "white" : Qt.darker("white", 2.0)
1439 }
1440
1441 Icon {
1442
1443=== added file 'PictureReview.qml'
1444--- PictureReview.qml 1970-01-01 00:00:00 +0000
1445+++ PictureReview.qml 2016-02-26 17:17:00 +0000
1446@@ -0,0 +1,46 @@
1447+/*
1448+ * Copyright (C) 2016 Canonical, Ltd.
1449+ *
1450+ * This program is free software; you can redistribute it and/or modify
1451+ * it under the terms of the GNU General Public License as published by
1452+ * the Free Software Foundation; version 3.
1453+ *
1454+ * This program is distributed in the hope that it will be useful,
1455+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1456+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1457+ * GNU General Public License for more details.
1458+ *
1459+ * You should have received a copy of the GNU General Public License
1460+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1461+ */
1462+
1463+import QtQuick 2.4
1464+import QtQuick.Window 2.2
1465+
1466+Item {
1467+ id: snapshotRoot
1468+ property alias source: image.source
1469+ property ViewFinderGeometry geometry
1470+ property bool loaded: image.status == Image.Ready
1471+
1472+ // Rotation is locked at the moment the picture is shoot
1473+ // (in case processing is long, such as with HDR)
1474+ function lockOrientation() {
1475+ image.rotation = Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
1476+ }
1477+
1478+ Image {
1479+ id: image
1480+ anchors.centerIn: parent
1481+ anchors.verticalCenterOffset: -geometry.y
1482+
1483+ asynchronous: true
1484+ cache: false
1485+ fillMode: Image.PreserveAspectFit
1486+ smooth: false
1487+ width: rotation == 0 ? geometry.width : geometry.height
1488+ height: rotation == 0 ? geometry.height : geometry.width
1489+ sourceSize.width: width
1490+ sourceSize.height: height
1491+ }
1492+}
1493
1494=== added file 'ProcessingFeedback.qml'
1495--- ProcessingFeedback.qml 1970-01-01 00:00:00 +0000
1496+++ ProcessingFeedback.qml 2016-02-26 17:17:00 +0000
1497@@ -0,0 +1,43 @@
1498+/*
1499+ * Copyright 2016 Canonical Ltd.
1500+ *
1501+ * This program is free software; you can redistribute it and/or modify
1502+ * it under the terms of the GNU General Public License as published by
1503+ * the Free Software Foundation; version 3.
1504+ *
1505+ * This program is distributed in the hope that it will be useful,
1506+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1507+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1508+ * GNU General Public License for more details.
1509+ *
1510+ * You should have received a copy of the GNU General Public License
1511+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1512+ */
1513+
1514+import QtQuick 2.4
1515+import Ubuntu.Components 1.3
1516+
1517+Item {
1518+ id: processingFeedback
1519+
1520+ property bool processing: false
1521+
1522+ Timer {
1523+ interval: 2000
1524+ running: processing
1525+ onTriggered: spinner.running = true
1526+ }
1527+
1528+ onProcessingChanged: if (!processing) spinner.running = false
1529+
1530+ ActivityIndicator {
1531+ id: spinner
1532+ opacity: running ? 1.0 : 0.0
1533+ Behavior on opacity {
1534+ OpacityAnimator {
1535+ duration: UbuntuAnimation.SnapDuration
1536+ easing: UbuntuAnimation.StandardEasing
1537+ }
1538+ }
1539+ }
1540+}
1541
1542=== modified file 'SharePopover.qml'
1543--- SharePopover.qml 2015-10-22 12:46:34 +0000
1544+++ SharePopover.qml 2016-02-26 17:17:00 +0000
1545@@ -17,7 +17,7 @@
1546 import QtQuick 2.4
1547 import Ubuntu.Components 1.3
1548 import Ubuntu.Components.Popups 1.3
1549-import Ubuntu.Content 0.1
1550+import Ubuntu.Content 1.3
1551
1552 PopupBase {
1553 property var transferContentType
1554@@ -31,17 +31,9 @@
1555 contentPeerPicker.peerSelected.connect(contentPeerSelected);
1556 }
1557
1558- // FIXME: ContentPeerPicker should either have a background or not, not half of one
1559- Rectangle {
1560- anchors.fill: parent
1561- color: theme.palette.normal.overlay
1562- }
1563-
1564 ContentPeerPicker {
1565 id: contentPeerPicker
1566 // FIXME: ContentPeerPicker should define an implicit size and not refer to its parent
1567- // FIXME: ContentPeerPicker should not be visible: false by default
1568- visible: true
1569 Component.onCompleted: {
1570 contentType = parent.transferContentType;
1571 }
1572
1573=== removed file 'Snapshot.qml'
1574--- Snapshot.qml 2015-10-22 12:21:14 +0000
1575+++ Snapshot.qml 1970-01-01 00:00:00 +0000
1576@@ -1,98 +0,0 @@
1577-/*
1578- * Copyright (C) 2012 Canonical, Ltd.
1579- *
1580- * This program is free software; you can redistribute it and/or modify
1581- * it under the terms of the GNU General Public License as published by
1582- * the Free Software Foundation; version 3.
1583- *
1584- * This program is distributed in the hope that it will be useful,
1585- * but WITHOUT ANY WARRANTY; without even the implied warranty of
1586- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1587- * GNU General Public License for more details.
1588- *
1589- * You should have received a copy of the GNU General Public License
1590- * along with this program. If not, see <http://www.gnu.org/licenses/>.
1591- */
1592-
1593-import QtQuick 2.4
1594-import QtQuick.Window 2.2
1595-import Ubuntu.Components 1.3
1596-
1597-Item {
1598- id: snapshotRoot
1599- property alias source: snapshot.source
1600- property alias sliding: shoot.running
1601- property int orientation
1602- property ViewFinderGeometry geometry
1603- property bool deviceDefaultIsPortrait: true
1604- property bool loading: snapshot.status == Image.Loading
1605-
1606- function startOutAnimation() {
1607- shoot.restart()
1608- }
1609-
1610- visible: false
1611-
1612- Item {
1613- id: container
1614- width: parent.width
1615- height: parent.height
1616-
1617- Image {
1618- id: snapshot
1619- anchors.centerIn: parent
1620- anchors.verticalCenterOffset: -geometry.y
1621- rotation: snapshotRoot.orientation * -1
1622-
1623- asynchronous: true
1624- cache: false
1625- fillMode: Image.PreserveAspectFit
1626- smooth: false
1627- width: deviceDefaultIsPortrait ? geometry.height : geometry.width
1628- height: deviceDefaultIsPortrait ? geometry.width : geometry.height
1629- sourceSize.width: width
1630- sourceSize.height: height
1631- }
1632-
1633- Image {
1634- id: shadow
1635-
1636- property bool rotated: (snapshot.rotation % 180) != 0
1637- height: rotated ? snapshot.width : snapshot.height
1638- width: units.gu(2)
1639- x: (container.width - (rotated ? snapshot.height : snapshot.width)) / 2 - width
1640- source: "assets/shadow.png"
1641- fillMode: Image.Stretch
1642- asynchronous: true
1643- cache: false
1644- }
1645- }
1646- property int orientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
1647- property var angleToOrientation: {0: "PORTRAIT",
1648- 90: "LANDSCAPE",
1649- 270: "INVERTED_LANDSCAPE"}
1650-
1651- SequentialAnimation {
1652- id: shoot
1653-
1654- PropertyAction { target: snapshotRoot; property: "visible"; value: true }
1655- PauseAnimation { duration: 150 }
1656- XAnimator {
1657- target: container
1658- to: angleToOrientation[orientationAngle] == "PORTRAIT" ? container.width + shadow.width : 0
1659- duration: UbuntuAnimation.BriskDuration
1660- easing: UbuntuAnimation.StandardEasing
1661- }
1662- YAnimator {
1663- target: container
1664- to: angleToOrientation[orientationAngle] == "LANDSCAPE" ? container.height + shadow.width :
1665- angleToOrientation[orientationAngle] == "INVERTED_LANDSCAPE" ? -(container.height + shadow.width) : 0
1666- duration: UbuntuAnimation.BriskDuration
1667- easing: UbuntuAnimation.StandardEasing
1668- }
1669- PropertyAction { target: snapshot; property: "source"; value: ""}
1670- PropertyAction { target: snapshotRoot; property: "visible"; value: false }
1671- PropertyAction { target: container; property: "x"; value: 0 }
1672- PropertyAction { target: container; property: "y"; value: 0 }
1673- }
1674-}
1675
1676=== modified file 'ViewFinderExportConfirmation.qml'
1677--- ViewFinderExportConfirmation.qml 2015-12-10 12:17:49 +0000
1678+++ ViewFinderExportConfirmation.qml 2016-02-26 17:17:00 +0000
1679@@ -22,39 +22,56 @@
1680
1681 property bool isVideo
1682 property string mediaPath
1683- property Snapshot snapshot
1684-
1685- function confirmExport(path) {
1686- viewFinder.visible = false;
1687- viewFinderOverlay.visible = false;
1688- mediaPath = path;
1689- if (!isVideo) snapshot.visible = true;
1690- visible = true;
1691- }
1692-
1693- function hide() {
1694- viewFinder.visible = true;
1695- viewFinderOverlay.visible = true;
1696- snapshot.source = "";
1697- snapshot.visible = false;
1698- visible = false;
1699- }
1700+ property bool waitingForPictureCapture: false
1701+
1702+ signal hideRequested()
1703+ signal showRequested()
1704+ property ViewFinderGeometry viewFinderGeometry
1705
1706 visible: false
1707
1708+ // For videos show immediately without waiting for the preview to load,
1709+ // since we will show a progress indicator instead of the preview
1710+ onMediaPathChanged: if (mediaPath && isVideo) showRequested()
1711+
1712+ function photoCaptureStarted() {
1713+ controls.item.lockPictureOrientation()
1714+ waitingForPictureCapture = true
1715+ }
1716+
1717 Loader {
1718+ id: controls
1719 anchors.fill: parent
1720 asynchronous: true
1721 sourceComponent: Component {
1722 Item {
1723+ function lockPictureOrientation() { pictureReview.lockOrientation() }
1724+
1725 VideoReview {
1726 id: videoReview
1727 anchors.fill: parent
1728 bottomMargin: buttons.height
1729- videoPath: mediaPath
1730+ videoPath: isVideo ? mediaPath : ""
1731 visible: isVideo
1732 }
1733
1734+ PictureReview {
1735+ id: pictureReview
1736+ anchors.fill: parent
1737+ visible: !isVideo
1738+ geometry: viewFinderGeometry
1739+ source: !isVideo ? mediaPath : ""
1740+
1741+ // Show export confirmation only when the snapshot is loaded to prevent the
1742+ // screen being black while the image loads
1743+ onLoadedChanged: {
1744+ if (loaded) {
1745+ viewFinderExportConfirmation.showRequested()
1746+ waitingForPictureCapture = false
1747+ }
1748+ }
1749+ }
1750+
1751 Item {
1752 id: buttons
1753 anchors.bottom: parent.bottom
1754@@ -74,7 +91,7 @@
1755 }
1756
1757 iconName: "reload"
1758- onClicked: viewFinderExportConfirmation.hide()
1759+ onClicked: hideRequested()
1760 }
1761
1762 CircleButton {
1763@@ -90,8 +107,9 @@
1764
1765 iconName: "ok"
1766 onClicked: {
1767- viewFinderExportConfirmation.hide();
1768+ hideRequested();
1769 main.exportContent([mediaPath]);
1770+ mediaPath = "";
1771 }
1772 }
1773
1774@@ -108,8 +126,9 @@
1775
1776 iconName: "close"
1777 onClicked: {
1778- viewFinderExportConfirmation.hide();
1779+ hideRequested();
1780 main.cancelExport();
1781+ mediaPath = "";
1782 }
1783 }
1784 }
1785
1786=== modified file 'ViewFinderOverlay.qml'
1787--- ViewFinderOverlay.qml 2016-01-11 15:07:58 +0000
1788+++ ViewFinderOverlay.qml 2016-02-26 17:17:00 +0000
1789@@ -32,6 +32,7 @@
1790 property real revealProgress: noSpaceHint.visible ? 1.0 : bottomEdge.progress
1791 property var controls: controls
1792 property var settings: settings
1793+ property bool readyForCapture
1794
1795 function showFocusRing(x, y) {
1796 focusRing.center = Qt.point(x, y);
1797@@ -217,12 +218,31 @@
1798 photoResolutionOptionsModel.insert(1, optionFitting);
1799 }
1800
1801+ // If resolution setting is not supported select the resolution automatically
1802 var photoResolution = settings["photoResolution" + camera.advanced.activeCameraIndex];
1803- // If resolution setting chosen is not supported select the fitting resolution
1804- if (photoResolution != optionFitting.value &&
1805- photoResolution != optionMaximum.value) {
1806- settings["photoResolution" + camera.advanced.activeCameraIndex] = optionFitting.value;
1807- }
1808+ if (!isResolutionAnOption(photoResolution)) {
1809+ settings["photoResolution" + camera.advanced.activeCameraIndex] = getAutomaticResolution();
1810+ }
1811+ }
1812+
1813+ function getAutomaticResolution() {
1814+ var fittingResolution = sizeToString(camera.advanced.fittingResolution);
1815+ var maximumResolution = sizeToString(camera.advanced.maximumResolution);
1816+ if (isResolutionAnOption(fittingResolution)) {
1817+ return fittingResolution;
1818+ } else {
1819+ return maximumResolution;
1820+ }
1821+ }
1822+
1823+ function isResolutionAnOption(resolution) {
1824+ for (var i=0; i<photoResolutionOptionsModel.count; i++) {
1825+ var option = photoResolutionOptionsModel.get(i);
1826+ if (option.value == resolution) {
1827+ return true;
1828+ }
1829+ }
1830+ return false;
1831 }
1832
1833 function updateResolutionOptions() {
1834@@ -249,9 +269,9 @@
1835 settings.videoResolution = sizeToString(camera.advanced.videoRecorderResolution);
1836 updateResolutionOptions();
1837
1838- // If no resolution has ever been chosen, select the one that fits the screen
1839+ // If no resolution has ever been chosen, select one automatically
1840 if (!hasPhotoResolutionSetting) {
1841- settings["photoResolution" + camera.advanced.activeCameraIndex] = sizeToString(camera.advanced.fittingResolution);
1842+ settings["photoResolution" + camera.advanced.activeCameraIndex] = getAutomaticResolution();
1843 }
1844 }
1845 }
1846@@ -288,16 +308,6 @@
1847 && !camera.photoCaptureInProgress
1848 opacity: enabled ? 1.0 : 0.3
1849
1850- Item {
1851- /* Use the 'trigger' feature of Panel so that tapping on the Panel
1852- has the same effect as tapping outside of it (bottomEdgeClose) */
1853- id: clickReceiver
1854- anchors.fill: parent
1855- function trigger() {
1856- optionsOverlayClose();
1857- }
1858- }
1859-
1860 /* At startup, opened is false and 'bottomEdge.height' is 0 until
1861 optionsOverlayLoader has finished loading. When that happens
1862 'bottomEdge.height' becomes non 0 and 'bottomEdge.position' which
1863@@ -321,6 +331,7 @@
1864 property bool available: true
1865 property bool visible: true
1866 property bool showInIndicators: true
1867+ property bool colorize: !positionSource.isPrecise
1868
1869 ListElement {
1870 icon: ""
1871@@ -492,7 +503,7 @@
1872 property string label: i18n.tr("SD")
1873 property bool isToggle: true
1874 property int selectedIndex: bottomEdge.indexForValue(removableStorageOptionsModel, settings.preferRemovableStorage)
1875- property bool available: application.removableStoragePresent
1876+ property bool available: StorageLocations.removableStoragePresent
1877 property bool visible: available
1878
1879 ListElement {
1880@@ -555,12 +566,12 @@
1881 }
1882 ]
1883
1884- /* FIXME: application.removableStoragePresent is not updated dynamically.
1885+ /* FIXME: StorageLocations.removableStoragePresent is not updated dynamically.
1886 Workaround that by reading it when the bottom edge is opened/closed.
1887 */
1888 Connections {
1889 target: bottomEdge
1890- onOpenedChanged: removableStorageOptionsModel.available = application.removableStoragePresent
1891+ onOpenedChanged: removableStorageOptionsModel.available = StorageLocations.removableStoragePresent
1892 }
1893
1894 function indexForValue(model, value) {
1895@@ -601,6 +612,24 @@
1896 }
1897 }
1898 }
1899+
1900+ triggerSize: units.gu(3)
1901+
1902+ Item {
1903+ /* Use the 'trigger' feature of Panel so that tapping on the Panel
1904+ can be acted upon */
1905+ id: clickReceiver
1906+ anchors.fill: parent
1907+ anchors.topMargin: -bottomEdge.triggerSize
1908+
1909+ function trigger() {
1910+ if (bottomEdge.opened) {
1911+ optionsOverlayClose();
1912+ } else {
1913+ bottomEdge.open();
1914+ }
1915+ }
1916+ }
1917 }
1918 }
1919
1920@@ -669,11 +698,11 @@
1921
1922 if (camera.captureMode == Camera.CaptureVideo) {
1923 if (main.contentExportMode) {
1924- camera.videoRecorder.outputLocation = application.temporaryLocation;
1925- } else if (application.removableStoragePresent && settings.preferRemovableStorage) {
1926- camera.videoRecorder.outputLocation = application.removableStorageVideosLocation;
1927+ camera.videoRecorder.outputLocation = StorageLocations.temporaryLocation;
1928+ } else if (StorageLocations.removableStoragePresent && settings.preferRemovableStorage) {
1929+ camera.videoRecorder.outputLocation = StorageLocations.removableStorageVideosLocation;
1930 } else {
1931- camera.videoRecorder.outputLocation = application.videosLocation;
1932+ camera.videoRecorder.outputLocation = StorageLocations.videosLocation;
1933 }
1934
1935 if (camera.videoRecorder.recorderState == CameraRecorder.StoppedState) {
1936@@ -684,11 +713,10 @@
1937 if (!main.contentExportMode) {
1938 shootFeedback.start();
1939 }
1940+ camera.photoCaptureInProgress = true;
1941 camera.imageCapture.setMetadata("Orientation", orientation);
1942 var position = positionSource.position;
1943- if (settings.gpsEnabled && positionSource.valid
1944- && position.latitudeValid
1945- && position.longitudeValid) {
1946+ if (settings.gpsEnabled && positionSource.isPrecise) {
1947 camera.imageCapture.setMetadata("GPSLatitude", position.coordinate.latitude);
1948 camera.imageCapture.setMetadata("GPSLongitude", position.coordinate.longitude);
1949 camera.imageCapture.setMetadata("GPSTimeStamp", position.timestamp);
1950@@ -698,13 +726,12 @@
1951 }
1952 }
1953
1954- camera.photoCaptureInProgress = true;
1955 if (main.contentExportMode) {
1956- camera.imageCapture.captureToLocation(application.temporaryLocation);
1957- } else if (application.removableStoragePresent && settings.preferRemovableStorage) {
1958- camera.imageCapture.captureToLocation(application.removableStoragePicturesLocation);
1959+ camera.imageCapture.captureToLocation(StorageLocations.temporaryLocation);
1960+ } else if (StorageLocations.removableStoragePresent && settings.preferRemovableStorage) {
1961+ camera.imageCapture.captureToLocation(StorageLocations.removableStoragePicturesLocation);
1962 } else {
1963- camera.imageCapture.captureToLocation(application.picturesLocation);
1964+ camera.imageCapture.captureToLocation(StorageLocations.picturesLocation);
1965 }
1966 }
1967 }
1968@@ -760,6 +787,11 @@
1969 id: positionSource
1970 updateInterval: 1000
1971 active: settings.gpsEnabled
1972+ property bool isPrecise: valid
1973+ && position.latitudeValid
1974+ && position.longitudeValid
1975+ && (!position.horizontalAccuracyValid ||
1976+ position.horizontalAccuracy <= 100)
1977 }
1978
1979 Connections {
1980@@ -800,7 +832,7 @@
1981 horizontalCenter: parent.horizontalCenter
1982 }
1983
1984- enabled: camera.imageCapture.ready && !storageMonitor.diskSpaceCriticallyLow
1985+ enabled: viewFinderOverlay.readyForCapture && !storageMonitor.diskSpaceCriticallyLow
1986 state: (camera.captureMode == Camera.CaptureVideo) ?
1987 ((camera.videoRecorder.recorderState == CameraRecorder.StoppedState) ? "record_off" : "record_on") :
1988 "camera"
1989@@ -878,15 +910,19 @@
1990
1991 MouseArea {
1992 id: manualFocusMouseArea
1993- anchors.fill: parent
1994- enabled: !camera.photoCaptureInProgress
1995+ anchors {
1996+ fill: parent
1997+ // Pinch gestures need more clearance at the edges of the screen, but
1998+ // tap to focus should be safe all the way to the edges themselves instead.
1999+ leftMargin: -bottomEdgeIndicators.height
2000+ rightMargin: -bottomEdgeIndicators.height
2001+ }
2002+ enabled: camera.focus.isFocusPointModeSupported(Camera.FocusPointCustom) &&
2003+ !camera.photoCaptureInProgress
2004 onClicked: {
2005 camera.manualFocus(mouse.x, mouse.y);
2006 mouse.accepted = false;
2007 }
2008- // FIXME: calling 'isFocusPointModeSupported' fails with
2009- // "Error: Unknown method parameter type: QDeclarativeCamera::FocusPointMode"
2010- //enabled: camera.focus.isFocusPointModeSupported(Camera.FocusPointCustom)
2011 }
2012 }
2013
2014@@ -941,16 +977,29 @@
2015 }
2016 }
2017
2018+ ProcessingFeedback {
2019+ anchors {
2020+ top: parent.top
2021+ topMargin: units.gu(2)
2022+ left: parent.left
2023+ leftMargin: units.gu(2)
2024+ }
2025+ processing: camera.photoCaptureInProgress
2026+ }
2027+
2028 StorageMonitor {
2029 id: storageMonitor
2030- location: (application.removableStoragePresent && settings.preferRemovableStorage) ?
2031- application.removableStorageLocation : application.videosLocation
2032+ location: (StorageLocations.removableStoragePresent && settings.preferRemovableStorage) ?
2033+ StorageLocations.removableStorageLocation : StorageLocations.videosLocation
2034 onDiskSpaceLowChanged: if (storageMonitor.diskSpaceLow && !storageMonitor.diskSpaceCriticallyLow) {
2035 PopupUtils.open(freeSpaceLowDialogComponent);
2036 }
2037 onDiskSpaceCriticallyLowChanged: if (storageMonitor.diskSpaceCriticallyLow) {
2038 camera.videoRecorder.stop();
2039 }
2040+ onIsWriteableChanged: if (!isWriteable && !diskSpaceLow && !main.contentExportMode) {
2041+ PopupUtils.open(readOnlyMediaDialogComponent);
2042+ }
2043 }
2044
2045 NoSpaceHint {
2046@@ -974,6 +1023,20 @@
2047 }
2048 }
2049
2050+ Component {
2051+ id: readOnlyMediaDialogComponent
2052+ Dialog {
2053+ id: readOnlyMediaDialog
2054+ objectName: "readOnlyMediaDialog"
2055+ title: i18n.tr("External storage not writeable")
2056+ text: i18n.tr("It does not seem possible to write to your external storage media. Trying to eject and insert it again might solve the issue, or you might need to format it.")
2057+ Button {
2058+ text: i18n.tr("Cancel")
2059+ onClicked: PopupUtils.close(readOnlyMediaDialog)
2060+ }
2061+ }
2062+ }
2063+
2064 Connections {
2065 id: permissionErrorMonitor
2066 property var currentPermissionsDialog: null
2067
2068=== modified file 'ViewFinderOverlayLoader.qml'
2069--- ViewFinderOverlayLoader.qml 2016-01-07 08:03:57 +0000
2070+++ ViewFinderOverlayLoader.qml 2016-02-26 17:17:00 +0000
2071@@ -24,6 +24,7 @@
2072 property real revealProgress: loader.item ? loader.item.revealProgress : 0
2073 property var controls: loader.item ? loader.item.controls : null
2074 property var settings: loader.item.settings
2075+ property bool readyForCapture
2076
2077 function showFocusRing(x, y) {
2078 loader.item.showFocusRing(x, y);
2079@@ -35,6 +36,8 @@
2080
2081 asynchronous: true
2082 Component.onCompleted: {
2083- loader.setSource("ViewFinderOverlay.qml", { "camera": loader.camera });
2084+ loader.setSource("ViewFinderOverlay.qml", { "camera": loader.camera,
2085+ "readyForCapture": Qt.binding(function() { return loader.readyForCapture})
2086+ });
2087 }
2088 }
2089
2090=== modified file 'ViewFinderView.qml'
2091--- ViewFinderView.qml 2016-01-14 15:55:52 +0000
2092+++ ViewFinderView.qml 2016-02-26 17:17:00 +0000
2093@@ -17,6 +17,7 @@
2094 import QtQuick 2.4
2095 import QtQuick.Window 2.2
2096 import Ubuntu.Components 1.3
2097+import Ubuntu.Components.Popups 1.3
2098 import QtMultimedia 5.0
2099 import CameraApp 0.1
2100 import QtGraphicalEffects 1.0
2101@@ -96,36 +97,41 @@
2102 property bool switchInProgress: false
2103 property bool photoCaptureInProgress: false
2104
2105+ onPhotoCaptureInProgressChanged: {
2106+ if (main.contentExportMode && camera.photoCaptureInProgress) {
2107+ viewFinderExportConfirmation.photoCaptureStarted();
2108+ }
2109+ }
2110+
2111 imageCapture {
2112 onReadyChanged: {
2113- if (camera.imageCapture.ready && main.transfer) {
2114- if (main.transfer.contentType === ContentType.Videos) {
2115- viewFinderView.captureMode = Camera.CaptureVideo;
2116- } else {
2117- viewFinderView.captureMode = Camera.CaptureStillImage;
2118+ if (camera.imageCapture.ready) {
2119+ if (camera.photoCaptureInProgress) {
2120+ if (photoRollHint.necessary && !main.transfer) photoRollHint.enable();
2121+ camera.photoCaptureInProgress = false;
2122+ }
2123+
2124+ if (main.transfer) {
2125+ if (main.transfer.contentType === ContentType.Videos) {
2126+ viewFinderView.captureMode = Camera.CaptureVideo;
2127+ } else {
2128+ viewFinderView.captureMode = Camera.CaptureStillImage;
2129+ }
2130 }
2131 }
2132 }
2133+
2134 onCaptureFailed: {
2135+ console.log("Image capture failed for request " + requestId + ": " + message);
2136 camera.photoCaptureInProgress = false;
2137- console.log("Capture failed for request " + requestId + ": " + message);
2138- }
2139- onImageCaptured: {
2140- snapshot.source = preview;
2141- if (!main.contentExportMode) {
2142- viewFinderOverlay.visible = true;
2143- snapshot.startOutAnimation();
2144- if (photoRollHint.necessary) {
2145- photoRollHint.enable();
2146- }
2147- }
2148- }
2149+ viewFinderOverlay.visible = true;
2150+ PopupUtils.open(captureFailedDialogComponent);
2151+ }
2152+
2153 onImageSaved: {
2154- if (main.contentExportMode) {
2155- viewFinderExportConfirmation.confirmExport(path);
2156- }
2157+ if (main.contentExportMode) viewFinderExportConfirmation.mediaPath = path;
2158+
2159 viewFinderView.photoTaken(path);
2160- camera.photoCaptureInProgress = false;
2161 metricPhotos.increment();
2162 console.log("Picture saved as " + path);
2163 }
2164@@ -135,15 +141,19 @@
2165 onRecorderStateChanged: {
2166 if (videoRecorder.recorderState === CameraRecorder.StoppedState) {
2167 metricVideos.increment()
2168- viewFinderOverlay.visible = true;
2169 viewFinderView.videoShot(videoRecorder.actualLocation);
2170 if (main.contentExportMode) {
2171- viewFinderExportConfirmation.confirmExport(videoRecorder.actualLocation);
2172+ viewFinderExportConfirmation.mediaPath = videoRecorder.actualLocation
2173 } else if (photoRollHint.necessary) {
2174 photoRollHint.enable();
2175 }
2176 }
2177 }
2178+ onErrorCodeChanged: {
2179+ if (videoRecorder.errorCode !== CameraRecorder.NoError) {
2180+ PopupUtils.open(captureFailedDialogComponent);
2181+ }
2182+ }
2183 }
2184 }
2185
2186@@ -235,18 +245,21 @@
2187 width: parent.width
2188 height: parent.height
2189 source: camera
2190+ opacity: ((main.contentExportMode && viewFinderExportConfirmation.waitingForPictureCapture) ||
2191+ (!main.contentExportMode && camera.photoCaptureInProgress && !camera.imageCapture.ready))
2192+ ? 0.1 : 1.0
2193
2194 /* This rotation need to be applied since the camera hardware in the
2195- Galaxy Nexus phone is mounted at an angle inside the device, so the video
2196- feed is rotated too.
2197- FIXME: This should come from a system configuration option so that we
2198- don't have to have a different codebase for each different device we want
2199- to run on. Android has that information and QML has an API to reflect it:
2200- the camera.orientation property. Unfortunately it is not hooked up yet.
2201+ Galaxy Nexus phone is mounted at an angle inside the device, so the video
2202+ feed is rotated too.
2203+ FIXME: This should come from a system configuration option so that we
2204+ don't have to have a different codebase for each different device we want
2205+ to run on. Android has that information and QML has an API to reflect it:
2206+ the camera.orientation property. Unfortunately it is not hooked up yet.
2207
2208- Ref.: http://doc.qt.io/qt-5/qml-qtmultimedia-camera.html#orientation-prop
2209- http://doc.qt.io/qt-5/qcamerainfocontrol.html#cameraOrientation
2210- http://developer.android.com/reference/android/hardware/Camera.CameraInfo.html#orientation
2211+ Ref.: http://doc.qt.io/qt-5/qml-qtmultimedia-camera.html#orientation-prop
2212+ http://doc.qt.io/qt-5/qcamerainfocontrol.html#cameraOrientation
2213+ http://developer.android.com/reference/android/hardware/Camera.CameraInfo.html#orientation
2214 */
2215 Component.onCompleted: {
2216 // Set orientation only at startup because later on Screen.primaryOrientation
2217@@ -254,14 +267,6 @@
2218 orientation = Screen.primaryOrientation === Qt.PortraitOrientation ? -90 : 0;
2219 }
2220
2221- /* Convenience item tracking the real position and size of the real video feed.
2222- Having this helps since these values depend on a lot of rules:
2223- - the feed is automatically scaled to fit the viewfinder
2224- - the viewfinder might apply a rotation to the feed, depending on device orientation
2225- - the resolution and aspect ratio of the feed changes depending on the active camera
2226- The item is also separated in a component so it can be unit tested.
2227- */
2228-
2229 transform: Rotation {
2230 origin.x: viewFinder.width / 2
2231 origin.y: viewFinder.height / 2
2232@@ -270,6 +275,13 @@
2233 }
2234 }
2235
2236+ /* Convenience item tracking the real position and size of the real video feed.
2237+ Having this helps since these values depend on a lot of rules:
2238+ - the feed is automatically scaled to fit the viewfinder
2239+ - the viewfinder might apply a rotation to the feed, depending on device orientation
2240+ - the resolution and aspect ratio of the feed changes depending on the active camera
2241+ The item is also separated in a component so it can be unit tested.
2242+ */
2243 ViewFinderGeometry {
2244 id: viewFinderGeometry
2245 anchors.centerIn: parent
2246@@ -330,12 +342,10 @@
2247 anchors.fill: parent
2248
2249 function start() {
2250- viewFinderOverlay.visible = false;
2251 }
2252
2253 function stop() {
2254 remainingSecsLabel.text = "";
2255- viewFinderOverlay.visible = true;
2256 }
2257
2258 function showRemainingSecs(secs) {
2259@@ -384,7 +394,6 @@
2260
2261 function start() {
2262 shootFeedback.opacity = 1.0;
2263- viewFinderOverlay.visible = false;
2264 shootFeedbackAnimation.restart();
2265 }
2266
2267@@ -393,7 +402,7 @@
2268 target: shootFeedback
2269 from: 1.0
2270 to: 0.0
2271- duration: 50
2272+ duration: UbuntuAnimation.SnapDuration
2273 easing: UbuntuAnimation.StandardEasing
2274 }
2275 }
2276@@ -410,38 +419,76 @@
2277 visible: radius !== 0
2278 }
2279
2280+ PhotoRollHint {
2281+ id: photoRollHint
2282+ anchors.fill: parent
2283+ visible: enabled
2284+
2285+ Connections {
2286+ target: viewFinderView
2287+ onInViewChanged: if (!viewFinderView.inView) photoRollHint.disable()
2288+ }
2289+ }
2290+
2291 ViewFinderOverlayLoader {
2292 id: viewFinderOverlay
2293
2294 anchors.fill: parent
2295 camera: camera
2296 opacity: status == Loader.Ready && overlayVisible && !photoRollHint.enabled ? 1.0 : 0.0
2297- Behavior on opacity {UbuntuNumberAnimation {duration: UbuntuAnimation.SnapDuration}}
2298- }
2299-
2300- PhotoRollHint {
2301- id: photoRollHint
2302- anchors.fill: parent
2303- visible: enabled && !snapshot.loading
2304-
2305- Connections {
2306- target: viewFinderView
2307- onInViewChanged: if (!viewFinderView.inView) photoRollHint.disable()
2308- }
2309- }
2310-
2311- Snapshot {
2312- id: snapshot
2313- anchors.fill: parent
2314- orientation: viewFinder.orientation
2315- geometry: viewFinderGeometry
2316- deviceDefaultIsPortrait: Screen.primaryOrientation === Qt.PortraitOrientation
2317+ readyForCapture: main.contentExportMode &&
2318+ viewFinderExportConfirmation.waitingForPictureCapture ? false : camera.imageCapture.ready
2319+
2320+ Behavior on opacity {
2321+ enabled: !photoRollHint.enabled
2322+ UbuntuNumberAnimation {duration: UbuntuAnimation.SnapDuration}
2323+ }
2324+
2325+ // Tapping anywhere on the screen should not trigger any camera
2326+ // controls while PhotoRoll hint is visible
2327+ MouseArea {
2328+ anchors.fill: parent
2329+ enabled: photoRollHint.visible
2330+ }
2331 }
2332
2333 ViewFinderExportConfirmation {
2334 id: viewFinderExportConfirmation
2335 anchors.fill: parent
2336- snapshot: snapshot
2337+
2338 isVideo: main.transfer.contentType == ContentType.Videos
2339+ viewFinderGeometry: viewFinderGeometry
2340+
2341+ onShowRequested: {
2342+ viewFinder.visible = false;
2343+ viewFinderOverlay.visible = false;
2344+ visible = true;
2345+ }
2346+
2347+ onHideRequested: {
2348+ viewFinder.visible = true;
2349+ viewFinderOverlay.visible = true;
2350+ visible = false;
2351+ }
2352+ }
2353+
2354+ Component {
2355+ id: captureFailedDialogComponent
2356+ Dialog {
2357+ id: captureFailedDialog
2358+ objectName: "captureFailedDialog"
2359+ title: i18n.tr("Capture failed")
2360+
2361+ // If we are capturing to an SD card the problem can be a broken card, otherwise it is probably a
2362+ // crash in the driver and a reboot might fix things.
2363+ text: StorageLocations.removableStorageLocation && viewFinderOverlay.settings.preferRemovableStorage ?
2364+ i18n.tr("Replacing your external media, formatting it, or restarting the device might fix the problem.") :
2365+ i18n.tr("Restarting your device might fix the problem.")
2366+
2367+ Button {
2368+ text: i18n.tr("Cancel")
2369+ onClicked: PopupUtils.close(captureFailedDialog)
2370+ }
2371+ }
2372 }
2373 }
2374
2375=== modified file 'cameraapplication.cpp'
2376--- cameraapplication.cpp 2015-11-25 17:47:00 +0000
2377+++ cameraapplication.cpp 2016-02-26 17:17:00 +0000
2378@@ -19,14 +19,8 @@
2379
2380 #include "cameraapplication.h"
2381
2382-#include <QtCore/QDir>
2383-#include <QtCore/QUrl>
2384 #include <QtCore/QDebug>
2385-#include <QtCore/QStringList>
2386 #include <QtCore/QLibrary>
2387-#include <QtCore/QStandardPaths>
2388-#include <QtCore/QDir>
2389-#include <QDate>
2390 #include <QQmlContext>
2391 #include <QQmlEngine>
2392 #include <QScreen>
2393@@ -95,103 +89,3 @@
2394
2395 return true;
2396 }
2397-
2398-QString CameraApplication::picturesLocation() const
2399-{
2400- QStringList locations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation);
2401- if (locations.isEmpty()) {
2402- return QString();
2403- }
2404- QString location = locations.at(0) + "/" + QCoreApplication::applicationName();
2405- QDir dir;
2406- // Transition from old directory 'camera' to new one; see bug #1363112
2407- // https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1363112
2408- dir.rename(locations.at(0) + "/" + "camera", location);
2409- dir.mkpath(location);
2410- return location;
2411-}
2412-
2413-QString CameraApplication::videosLocation() const
2414-{
2415- QStringList locations = QStandardPaths::standardLocations(QStandardPaths::MoviesLocation);
2416- if (locations.isEmpty()) {
2417- return QString();
2418- }
2419- QString location = locations.at(0) + "/" + QCoreApplication::applicationName();
2420- QDir dir;
2421- // Transition from old directory 'camera' to new one; see bug #1363112
2422- // https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1363112
2423- dir.rename(locations.at(0) + "/" + "camera", location);
2424- dir.mkpath(location);
2425- return location;
2426-}
2427-
2428-QString CameraApplication::temporaryLocation() const
2429-{
2430- QStringList locations = QStandardPaths::standardLocations(QStandardPaths::TempLocation);
2431- if (locations.isEmpty()) {
2432- return QString();
2433- }
2434- QString location = locations.at(0);
2435- QDir dir;
2436- dir.mkpath(location);
2437- return location;
2438-}
2439-
2440-bool CameraApplication::removableStoragePresent() const
2441-{
2442- return !removableStorageLocation().isEmpty();
2443-}
2444-
2445-QString CameraApplication::removableStorageLocation() const
2446-{
2447- /* FIXME: when Qt5.4 is available, switch to using newly introduced
2448- * QStorageInfo API.
2449- * Ref.: http://doc-snapshot.qt-project.org/qt5-5.4/qstorageinfo.html
2450- */
2451- QString userName = qgetenv("USER");
2452- QDir media("/media/" + userName);
2453- QStringList mediaDirs = media.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
2454-
2455- if (mediaDirs.size() > 0) {
2456- return QString("/media/" + userName + "/" + mediaDirs.at(0));
2457- } else {
2458- return QString();
2459- }
2460-}
2461-
2462-QString CameraApplication::removableStoragePicturesLocation() const
2463-{
2464- QString storageLocation = removableStorageLocation();
2465- if (storageLocation.isEmpty()) {
2466- return QString();
2467- }
2468-
2469- QStringList locations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation);
2470- QString pictureDir = QString(locations.at(0)).split("/").value(3);
2471- if (pictureDir.isEmpty()){
2472- return QString();
2473- }
2474- QString location = storageLocation + "/" + pictureDir + "/" + QCoreApplication::applicationName();
2475- QDir dir;
2476- dir.mkpath(location);
2477- return location;
2478-}
2479-
2480-QString CameraApplication::removableStorageVideosLocation() const
2481-{
2482- QString storageLocation = removableStorageLocation();
2483- if (storageLocation.isEmpty()) {
2484- return QString();
2485- }
2486-
2487- QStringList locations = QStandardPaths::standardLocations(QStandardPaths::MoviesLocation);
2488- QString movieDir = QString(locations.at(0)).split("/").value(3);
2489- if (movieDir.isEmpty()){
2490- return QString();
2491- }
2492- QString location = storageLocation + "/" + movieDir + "/" + QCoreApplication::applicationName();
2493- QDir dir;
2494- dir.mkpath(location);
2495- return location;
2496-}
2497
2498=== modified file 'cameraapplication.h'
2499--- cameraapplication.h 2015-11-25 17:47:00 +0000
2500+++ cameraapplication.h 2016-02-26 17:17:00 +0000
2501@@ -29,29 +29,14 @@
2502 {
2503 Q_OBJECT
2504 Q_PROPERTY(bool desktopMode READ isDesktopMode CONSTANT)
2505- Q_PROPERTY(QString picturesLocation READ picturesLocation CONSTANT)
2506- Q_PROPERTY(QString videosLocation READ videosLocation CONSTANT)
2507- Q_PROPERTY(QString temporaryLocation READ temporaryLocation CONSTANT)
2508- Q_PROPERTY(bool removableStoragePresent READ removableStoragePresent NOTIFY removableStoragePresentChanged)
2509- Q_PROPERTY(QString removableStorageLocation READ removableStorageLocation CONSTANT)
2510- Q_PROPERTY(QString removableStoragePicturesLocation READ removableStoragePicturesLocation CONSTANT)
2511- Q_PROPERTY(QString removableStorageVideosLocation READ removableStorageVideosLocation CONSTANT)
2512
2513 public:
2514 CameraApplication(int &argc, char **argv);
2515 virtual ~CameraApplication();
2516 bool setup();
2517 bool isDesktopMode() const;
2518- QString picturesLocation() const;
2519- QString videosLocation() const;
2520- QString temporaryLocation() const;
2521- bool removableStoragePresent() const;
2522- QString removableStorageLocation() const;
2523- QString removableStoragePicturesLocation() const;
2524- QString removableStorageVideosLocation() const;
2525
2526 Q_SIGNALS:
2527- void removableStoragePresentChanged();
2528
2529 private:
2530 QScopedPointer<QQmlApplicationEngine> m_engine;
2531
2532=== modified file 'debian/rules'
2533--- debian/rules 2015-11-18 06:44:35 +0000
2534+++ debian/rules 2016-02-26 17:17:00 +0000
2535@@ -11,10 +11,6 @@
2536 override_dh_auto_configure:
2537 dh_auto_configure -- -DCLICK_MODE=OFF -DINSTALL_TESTS=ON
2538
2539-override_dh_auto_test:
2540- python3 -m flake8 .
2541- dh_auto_test
2542-
2543 override_dh_install:
2544 dh_install --fail-missing
2545
2546
2547=== modified file 'manifest.json.in'
2548--- manifest.json.in 2015-11-25 17:56:59 +0000
2549+++ manifest.json.in 2016-02-26 17:17:00 +0000
2550@@ -1,6 +1,6 @@
2551 {
2552 "description": "An application to take pictures and videos with the device cameras",
2553- "framework": "ubuntu-sdk-15.04.3",
2554+ "framework": "ubuntu-sdk-15.04.4",
2555 "architecture": "@CLICK_ARCH@",
2556 "hooks": {
2557 "camera": {
2558
2559=== removed file 'run_tests.sh'
2560--- run_tests.sh 2012-10-24 15:18:16 +0000
2561+++ run_tests.sh 1970-01-01 00:00:00 +0000
2562@@ -1,13 +0,0 @@
2563-#!/bin/bash
2564-export PATH=/opt/qt5/bin:$PATH
2565-cd tests/autopilot
2566-
2567-echo running with arg: $1
2568-
2569-if [ "$1" == "" ]; then
2570- autopilot run camera_app
2571-else
2572- autopilot run -o ../../$1 -f xml -r -rd ../../ camera_app
2573-fi
2574-
2575-exit 0
2576
2577=== modified file 'tests/autopilot/camera_app/tests/test_focus.py'
2578--- tests/autopilot/camera_app/tests/test_focus.py 2015-10-05 13:14:12 +0000
2579+++ tests/autopilot/camera_app/tests/test_focus.py 2016-02-26 17:17:00 +0000
2580@@ -28,6 +28,20 @@
2581 def tearDown(self):
2582 super(TestFocus, self).tearDown()
2583
2584+ def verify_focus_ring_after_click_at(self, ring, x, y):
2585+ # The focus ring should be invisible in the beginning
2586+ self.assertThat(ring.opacity, Eventually(Equals(0.0)))
2587+
2588+ # Click in the designated spot
2589+ self.pointing_device.move(x, y)
2590+ self.pointing_device.click()
2591+
2592+ # The focus ring sould be visible now
2593+ self.assertThat(ring.opacity, Eventually(GreaterThan(0.5)))
2594+
2595+ # After some seconds the focus ring should fade out
2596+ self.assertThat(ring.opacity, Eventually(Equals(0.0)))
2597+
2598 """Test focusing in an area where we know the picture is"""
2599 @unittest.skipIf(model() == 'Galaxy Nexus', 'Unusable with Mir on maguro')
2600 def test_focus_valid_and_disappear(self):
2601@@ -36,21 +50,16 @@
2602 switch_cameras = self.main_window.get_swap_camera_button()
2603 exposure_button = self.main_window.get_exposure_button()
2604
2605- # The focus ring should be invisible in the beginning
2606- self.assertThat(focus_ring.opacity, Eventually(Equals(0.0)))
2607-
2608- self.pointing_device.move_to_object(feed)
2609- self.pointing_device.click()
2610- click_coords = list(self.pointing_device.position())
2611-
2612- # The focus ring sould be visible and centered to the mouse click
2613- # coords now
2614- # focus_ring_center = self.get_center(focus_ring)
2615- self.assertThat(focus_ring.opacity, Eventually(GreaterThan(0.5)))
2616-# self.assertEquals(focus_ring_center, click_coords)
2617-
2618- # After some seconds the focus ring should fade out
2619- self.assertThat(focus_ring.opacity, Eventually(Equals(0.0)))
2620+ # Click in the center of the viewfinder area
2621+ mid_x, mid_y = self.get_center(feed)
2622+ self.verify_focus_ring_after_click_at(focus_ring, mid_x, mid_y)
2623+
2624+ # Then try on the side edges and top edge to verify they
2625+ # are focusable too
2626+ self.verify_focus_ring_after_click_at(focus_ring, 1, mid_y)
2627+ self.verify_focus_ring_after_click_at(focus_ring, feed.width - 1,
2628+ mid_y)
2629+ self.verify_focus_ring_after_click_at(focus_ring, mid_x, 1)
2630
2631 # Switch cameras, wait for camera to settle, and try again
2632 self.pointing_device.move_to_object(switch_cameras)
2633@@ -58,19 +67,14 @@
2634 self.assertThat(exposure_button.enabled, Eventually(Equals(True)))
2635
2636 # Click in the center of the viewfinder area
2637- click_coords = [feed.globalRect[2] // 2 + feed.globalRect[0],
2638- feed.globalRect[3] // 2 + feed.globalRect[1]]
2639- self.pointing_device.move(click_coords[0], click_coords[1])
2640- self.pointing_device.click()
2641-
2642- # The focus ring sould be visible and centered to the mouse
2643- # click coords now
2644- # focus_ring_center = self.get_center(focus_ring)
2645- self.assertThat(focus_ring.opacity, Eventually(GreaterThan(0.5)))
2646-# self.assertEquals(focus_ring_center, click_coords)
2647-
2648- # After some seconds the focus ring should fade out
2649- self.assertThat(focus_ring.opacity, Eventually(Equals(0.0)))
2650+ self.verify_focus_ring_after_click_at(focus_ring, mid_x, mid_y)
2651+
2652+ # Then try on the side edges and top edge to verify they
2653+ # are focusable too
2654+ self.verify_focus_ring_after_click_at(focus_ring, 1, mid_y)
2655+ self.verify_focus_ring_after_click_at(focus_ring, feed.width - 1,
2656+ mid_y)
2657+ self.verify_focus_ring_after_click_at(focus_ring, mid_x, 1)
2658
2659 @unittest.skipIf(model() == 'Galaxy Nexus', 'Unusable with Mir on maguro')
2660 def test_focus_invalid(self):
2661
2662=== modified file 'tests/autopilot/camera_app/tests/test_options.py'
2663--- tests/autopilot/camera_app/tests/test_options.py 2015-11-20 15:01:02 +0000
2664+++ tests/autopilot/camera_app/tests/test_options.py 2016-02-26 17:17:00 +0000
2665@@ -42,6 +42,33 @@
2666 # check overlay is closed
2667 self.assertThat(bottom_edge.opened, Eventually(Equals(False)))
2668
2669+ # try opening and closing by tapping on the bottom of the viewfinder
2670+ bottom_edge = self.main_window.get_bottom_edge()
2671+ bottom_edge.open()
2672+
2673+ # check overlay is opened
2674+ self.assertThat(bottom_edge.opened, Eventually(Equals(True)))
2675+
2676+ # tap on the bottom of the viewfinder to close overlay
2677+ viewfinder = self.main_window.get_viewfinder()
2678+ x = viewfinder.globalRect.x + viewfinder.width / 2.0
2679+ y = viewfinder.globalRect.y + viewfinder.height - 1.0
2680+ self.pointing_device.move(x, y)
2681+ self.pointing_device.click()
2682+
2683+ # check overlay is closed
2684+ self.assertThat(bottom_edge.opened, Eventually(Equals(False)))
2685+
2686+ """Test that the options overlay opens properly by tapping on the hint"""
2687+ def test_overlay_open_tapping_hint(self):
2688+ options_hint = self.app.wait_select_single(objectName="indicatorsRow")
2689+ self.pointing_device.move_to_object(options_hint)
2690+ self.pointing_device.click()
2691+
2692+ # check overlay is opened
2693+ bottom_edge = self.main_window.get_bottom_edge()
2694+ self.assertThat(bottom_edge.opened, Eventually(Equals(True)))
2695+
2696 """Test toggling on/off grid lines option"""
2697 def test_toggle_grid_lines(self):
2698 gridlines = self.app.wait_select_single(
2699
2700=== modified file 'tests/unittests/CMakeLists.txt'
2701--- tests/unittests/CMakeLists.txt 2015-03-12 22:36:12 +0000
2702+++ tests/unittests/CMakeLists.txt 2016-02-26 17:17:00 +0000
2703@@ -33,6 +33,7 @@
2704
2705 qt5_use_modules(tst_storagemonitor Widgets Core Quick Qml Test)
2706 add_test(tst_storagemonitor tst_storagemonitor -xunitxml -o test.xml)
2707+add_test(flake8 python3 -m flake8 ${CMAKE_SOURCE_DIR}/tests/autopilot)
2708 set_tests_properties(tst_storagemonitor PROPERTIES
2709 TIMEOUT ${CTEST_TESTING_TIMEOUT}
2710 ENVIRONMENT "QT_QPA_PLATFORM=minimal"
2711
2712=== modified file 'tests/unittests/qstorageinfo_stub.cpp'
2713--- tests/unittests/qstorageinfo_stub.cpp 2015-01-26 10:57:36 +0000
2714+++ tests/unittests/qstorageinfo_stub.cpp 2016-02-26 17:17:00 +0000
2715@@ -14,14 +14,23 @@
2716 * along with this program. If not, see <http://www.gnu.org/licenses/>.
2717 */
2718
2719-#include "qstorageinfo.h"
2720-#include "qstorageinfo_p.h"
2721+#include <QStorageInfo>
2722 #include "storageinfocontrol.h"
2723
2724 #include <QDebug>
2725
2726 QString location;
2727
2728+class QStorageInfoPrivateRef {
2729+public:
2730+ bool deref() { return false; }
2731+};
2732+
2733+class QStorageInfoPrivate {
2734+public:
2735+ QStorageInfoPrivateRef ref;
2736+};
2737+
2738 QStorageInfo::QStorageInfo()
2739 {
2740 }
2741@@ -47,6 +56,11 @@
2742 location = path;
2743 }
2744
2745+QString QStorageInfo::rootPath() const
2746+{
2747+ return location;
2748+}
2749+
2750 qint64 QStorageInfo::bytesAvailable() const
2751 {
2752 return StorageInfoControl::instance()->freeSpaceMap[location];

Subscribers

People subscribed via source and target branches