Merge lp:~stolowski/unity-scopes-shell/location-changes into lp:unity-scopes-shell

Proposed by Paweł Stołowski
Status: Merged
Approved by: Marcus Tomlinson
Approved revision: 320
Merged at revision: 328
Proposed branch: lp:~stolowski/unity-scopes-shell/location-changes
Merge into: lp:unity-scopes-shell
Diff against target: 1800 lines (+461/-742)
23 files modified
CMakeLists.txt (+2/-2)
debian/control (+3/-7)
debian/control.in (+1/-5)
po/POTFILES.in (+11/-4)
src/CMakeLists.txt (+1/-7)
src/Unity/CMakeLists.txt (+1/-1)
src/Unity/locationaccesshelper.cpp (+105/-0)
src/Unity/locationaccesshelper.h (+56/-0)
src/Unity/locationservice.cpp (+0/-26)
src/Unity/locationservice.h (+0/-72)
src/Unity/overviewscope.cpp (+1/-1)
src/Unity/overviewscope.h (+1/-1)
src/Unity/scope.cpp (+46/-9)
src/Unity/scope.h (+7/-5)
src/Unity/scopes.cpp (+27/-5)
src/Unity/scopes.h (+7/-3)
src/Unity/settingsmodel.cpp (+8/-1)
src/Unity/settingsmodel.h (+2/-1)
src/Unity/ubuntulocationservice.cpp (+132/-269)
src/Unity/ubuntulocationservice.h (+50/-20)
tests/CMakeLists.txt (+0/-3)
tests/geoip.ubuntu.com.py (+0/-66)
tests/locationtest.cpp (+0/-234)
To merge this branch: bzr merge lp:~stolowski/unity-scopes-shell/location-changes
Reviewer Review Type Date Requested Status
Marcus Tomlinson (community) Approve
PS Jenkins bot continuous-integration Pending
Review via email: mp+285332@code.launchpad.net

Commit message

Use Qt Location API.

Description of the change

Use Qt Location API. It should be tested with corresponding unity8 branch that makes the Dash run confined with apparmor.

I've added a new section to the test plan https://wiki.ubuntu.com/Process/Merges/TestPlan/scopes to cover these changes.

To post a comment you must log in.
Revision history for this message
Marcus Tomlinson (marcustomlinson) wrote :

Looks good, although one observation:

I have Nearby favourited. Once I have enabled location access to scopes and I restart the phone, the scope says "Please enable location..." again. I also notice incorrect weather shown on the Today scope. There's obviously a race in scopes startup and the location becoming available/accessible/enabled. Not sure how easy this would be to fix, but it is a bit confusing.

+ For some reason I can't edit the test plan page anymore, so I'll just add review comments for that here:

1) "for testing purposes, remove home/phablet/.local/share/UbuntuLocationService/trust.db"

missing a / before "home"

2) Select 'Deny'.

should say "Select 'Don't Allow'."

review: Needs Fixing
Revision history for this message
Paweł Stołowski (stolowski) wrote :

> Looks good, although one observation:
>
> I have Nearby favourited. Once I have enabled location access to scopes and I
> restart the phone, the scope says "Please enable location..." again. I also
> notice incorrect weather shown on the Today scope. There's obviously a race in
> scopes startup and the location becoming available/accessible/enabled. Not
> sure how easy this would be to fix, but it is a bit confusing.

Right. I've missed that during my refactoring. The implementation should handle this now, however for some reason I'm always getting error (access denied) from Qt location API on the initial request when Dash is starting. I asked Thomas to check the backend.

>
> + For some reason I can't edit the test plan page anymore, so I'll just add
> review comments for that here:
>
> 1) "for testing purposes, remove
> home/phablet/.local/share/UbuntuLocationService/trust.db"
>
> missing a / before "home"
>
> 2) Select 'Deny'.
>
> should say "Select 'Don't Allow'."

Ok, the test plan has been fixed.

Revision history for this message
Marcus Tomlinson (marcustomlinson) wrote :

Something weird is happening now. When I select "Don't Allow" location access, scopes that use location appear completely blank (Nothing. Not even a title.).

Looks like we're stuck in a retry loop or something. unity8-dash.log: http://pastebin.ubuntu.com/16941491/

When I enable location access, all scopes work fine again. After disabling location access and rebooting, I get the same blank scope behaviour again :(

review: Needs Fixing
Revision history for this message
Paweł Stołowski (stolowski) wrote :

> Something weird is happening now. When I select "Don't Allow" location access,
> scopes that use location appear completely blank (Nothing. Not even a title.).
>
> Looks like we're stuck in a retry loop or something. unity8-dash.log:
> http://pastebin.ubuntu.com/16941491/
>
> When I enable location access, all scopes work fine again. After disabling
> location access and rebooting, I get the same blank scope behaviour again :(

Okay, that should be fixed now.

Revision history for this message
Marcus Tomlinson (marcustomlinson) wrote :

Awesome, so apart from the denied location access on startup, this looks good!

review: Approve
Revision history for this message
Marcus Tomlinson (marcustomlinson) wrote :

:P Oops! As discussed on IRC: the location access request occurs on setActive(), which causes a weird situation where a user that lands on say: Today or Nearby won't ever get location-specific data if all he/she did was pull to refresh over and over.

review: Needs Fixing
Revision history for this message
Marcus Tomlinson (marcustomlinson) wrote :

Yep, that does it :) Thanks for the perseverance!

review: Approve
Revision history for this message
Paweł Stołowski (stolowski) wrote :

Simplified the logic to show location prompt immediately on first user-initiated search so long as affected scope needs location. This makes it less confusing. Setting to needing review again.

Revision history for this message
Marcus Tomlinson (marcustomlinson) wrote :

> Simplified the logic to show location prompt immediately on first user-
> initiated search so long as affected scope needs location. This makes it less
> confusing. Setting to needing review again.

This is even better! Excellent job Pawel. Looks great.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2016-05-05 09:40:46 +0000
+++ CMakeLists.txt 2016-06-10 15:06:19 +0000
@@ -47,6 +47,7 @@
47find_package(Qt5Qml)47find_package(Qt5Qml)
48find_package(Qt5Quick)48find_package(Qt5Quick)
49find_package(Qt5Gui)49find_package(Qt5Gui)
50find_package(Qt5Positioning)
50find_package(Qt5Test)51find_package(Qt5Test)
51find_package(Boost COMPONENTS regex REQUIRED)52find_package(Boost COMPONENTS regex REQUIRED)
5253
@@ -54,7 +55,6 @@
54pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=12)55pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=12)
5556
56pkg_check_modules(GSETTINGSQT REQUIRED gsettings-qt)57pkg_check_modules(GSETTINGSQT REQUIRED gsettings-qt)
57pkg_check_modules(UBUNTU_LOCATION_SERVICE REQUIRED ubuntu-location-service)
58pkg_check_modules(ONLINE_ACCOUNTS_CLIENT REQUIRED OnlineAccountsClient)58pkg_check_modules(ONLINE_ACCOUNTS_CLIENT REQUIRED OnlineAccountsClient)
5959
60# Standard install paths60# Standard install paths
@@ -122,6 +122,6 @@
122add_subdirectory(src)122add_subdirectory(src)
123add_subdirectory(tests)123add_subdirectory(tests)
124add_subdirectory(data)124add_subdirectory(data)
125add_subdirectory(tools)125#add_subdirectory(tools)
126add_subdirectory(po)126add_subdirectory(po)
127add_subdirectory(docs)127add_subdirectory(docs)
128128
=== modified file 'debian/control'
--- debian/control 2016-05-25 08:16:27 +0000
+++ debian/control 2016-06-10 15:06:19 +0000
@@ -13,17 +13,13 @@
13 libgsettings-qt-dev (>= 0.1),13 libgsettings-qt-dev (>= 0.1),
14 libqtdbustest1-dev (>= 0.2),14 libqtdbustest1-dev (>= 0.2),
15 libqtdbusmock1-dev (>= 0.2),15 libqtdbusmock1-dev (>= 0.2),
16 libubuntu-location-service-dev (>= 2.0.1),
17 pkg-config,16 pkg-config,
18 python3-dev,17 python3-dev,
19# the two python- lines should be removed once lp bug #1582280 is fixed
20 python-singledispatch | python-tornado (<< 4.3.0),
21 python-backports-abc | python-tornado (<< 4.3.0),
22 python-tornado,
23 python3-sphinx,18 python3-sphinx,
24 qtdeclarative5-dev,19 qtdeclarative5-dev,
25 qtdeclarative5-dev-tools,20 qtdeclarative5-dev-tools,
26 qtdeclarative5-qtquick2-plugin,21 qtdeclarative5-qtquick2-plugin,
22 qtpositioning5-dev,
27 unity-schemas (>= 7.3.1),23 unity-schemas (>= 7.3.1),
28 libonline-accounts-client-dev,24 libonline-accounts-client-dev,
29 intltool,25 intltool,
@@ -61,7 +57,7 @@
61Description: QML plugin for Scopes57Description: QML plugin for Scopes
62 Plugin to integrate scopes with the Unity shell58 Plugin to integrate scopes with the Unity shell
6359
64Package: libscope-harness360Package: libscope-harness2
65Section: libdevel61Section: libdevel
66Architecture: any62Architecture: any
67Multi-Arch: same63Multi-Arch: same
@@ -77,7 +73,7 @@
77Multi-Arch: same73Multi-Arch: same
78Depends: ${misc:Depends},74Depends: ${misc:Depends},
79 ${shlibs:Depends},75 ${shlibs:Depends},
80 libscope-harness3 (= ${binary:Version})76 libscope-harness2 (= ${binary:Version})
81Description: Test harness for Unity scopes77Description: Test harness for Unity scopes
82 Drive Unity scopes with a simple synchronous API. Make assertions78 Drive Unity scopes with a simple synchronous API. Make assertions
83 about results. Development files.79 about results. Development files.
8480
=== modified file 'debian/control.in'
--- debian/control.in 2016-05-25 08:16:18 +0000
+++ debian/control.in 2016-06-10 15:06:19 +0000
@@ -13,17 +13,13 @@
13 libgsettings-qt-dev (>= 0.1),13 libgsettings-qt-dev (>= 0.1),
14 libqtdbustest1-dev (>= 0.2),14 libqtdbustest1-dev (>= 0.2),
15 libqtdbusmock1-dev (>= 0.2),15 libqtdbusmock1-dev (>= 0.2),
16 libubuntu-location-service-dev (>= 2.0.1),
17 pkg-config,16 pkg-config,
18 python3-dev,17 python3-dev,
19# the two python- lines should be removed once lp bug #1582280 is fixed
20 python-singledispatch | python-tornado (<< 4.3.0),
21 python-backports-abc | python-tornado (<< 4.3.0),
22 python-tornado,
23 python3-sphinx,18 python3-sphinx,
24 qtdeclarative5-dev,19 qtdeclarative5-dev,
25 qtdeclarative5-dev-tools,20 qtdeclarative5-dev-tools,
26 qtdeclarative5-qtquick2-plugin,21 qtdeclarative5-qtquick2-plugin,
22 qtpositioning5-dev,
27 unity-schemas (>= 7.3.1),23 unity-schemas (>= 7.3.1),
28 libonline-accounts-client-dev,24 libonline-accounts-client-dev,
29 intltool,25 intltool,
3026
=== modified file 'po/POTFILES.in'
--- po/POTFILES.in 2016-03-30 09:43:27 +0000
+++ po/POTFILES.in 2016-06-10 15:06:19 +0000
@@ -6,11 +6,10 @@
6tests/previewtest.cpp6tests/previewtest.cpp
7tests/overviewtest.cpp7tests/overviewtest.cpp
8tests/departmentstest.cpp8tests/departmentstest.cpp
9tests/locationtest.cpp
10tests/filtersendtoendtest.cpp9tests/filtersendtoendtest.cpp
10tests/scopesinittest.cpp
11tests/favoritestest.cpp11tests/favoritestest.cpp
12tests/optionselectorfiltertest.cpp12tests/optionselectorfiltertest.cpp
13tests/data/scopes/scopes.cpp
14tests/data/mock-scope-departments/mock-scope-departments.cpp13tests/data/mock-scope-departments/mock-scope-departments.cpp
15tests/data/mock-scope-filters/mock-scope-filters.cpp14tests/data/mock-scope-filters/mock-scope-filters.cpp
16tests/data/mock-scope-ttl/mock-scope-ttl.cpp15tests/data/mock-scope-ttl/mock-scope-ttl.cpp
@@ -64,6 +63,7 @@
64src/python/scope_harness/results-view-py.cpp63src/python/scope_harness/results-view-py.cpp
65src/python/scope_harness/preview-widget-py.cpp64src/python/scope_harness/preview-widget-py.cpp
66src/python/scope_harness/preview-widget-list-py.cpp65src/python/scope_harness/preview-widget-list-py.cpp
66src/Unity/rangeinputfilter.cpp
67src/Unity/previewwidgetmodel.cpp67src/Unity/previewwidgetmodel.cpp
68src/Unity/departmentnode.cpp68src/Unity/departmentnode.cpp
69src/Unity/logintoaccount.cpp69src/Unity/logintoaccount.cpp
@@ -75,16 +75,19 @@
75src/Unity/geoip.cpp75src/Unity/geoip.cpp
76src/Unity/plugin.cpp76src/Unity/plugin.cpp
77src/Unity/scopes.cpp77src/Unity/scopes.cpp
78src/Unity/filtergroupwidget.cpp
78src/Unity/filters.cpp79src/Unity/filters.cpp
79src/Unity/optionselectoroptions.cpp80src/Unity/optionselectoroptions.cpp
81src/Unity/locationaccesshelper.cpp
80src/Unity/settingsmodel.cpp82src/Unity/settingsmodel.cpp
81src/Unity/utils.cpp83src/Unity/utils.cpp
82src/Unity/optionselectorfilter.cpp84src/Unity/optionselectorfilter.cpp
83src/Unity/resultsmap.cpp85src/Unity/resultsmap.cpp
84src/Unity/locationservice.cpp
85src/Unity/ubuntulocationservice.cpp86src/Unity/ubuntulocationservice.cpp
87src/Unity/valueslidervalues.cpp
86src/Unity/iconutils.cpp88src/Unity/iconutils.cpp
87src/Unity/department.cpp89src/Unity/department.cpp
90src/Unity/valuesliderfilter.cpp
88src/Unity/categories.cpp91src/Unity/categories.cpp
89src/Unity/overviewresults.cpp92src/Unity/overviewresults.cpp
90src/Unity/resultsmodel.cpp93src/Unity/resultsmodel.cpp
@@ -134,16 +137,20 @@
134src/Unity/overviewresults.h137src/Unity/overviewresults.h
135src/Unity/plugin.h138src/Unity/plugin.h
136src/Unity/department.h139src/Unity/department.h
140src/Unity/valueslidervalues.h
141src/Unity/locationaccesshelper.h
137src/Unity/scope.h142src/Unity/scope.h
143src/Unity/rangeinputfilter.h
138src/Unity/filters.h144src/Unity/filters.h
139src/Unity/logintoaccount.h145src/Unity/logintoaccount.h
140src/Unity/overviewcategories.h146src/Unity/overviewcategories.h
141src/Unity/modelupdate.h147src/Unity/modelupdate.h
142src/Unity/locationservice.h
143src/Unity/overviewscope.h148src/Unity/overviewscope.h
144src/Unity/settingsmodel.h149src/Unity/settingsmodel.h
150src/Unity/valuesliderfilter.h
145src/Unity/resultsmodel.h151src/Unity/resultsmodel.h
146src/Unity/geoip.h152src/Unity/geoip.h
153src/Unity/filtergroupwidget.h
147src/Unity/resultsmap.h154src/Unity/resultsmap.h
148src/Unity/collectors.h155src/Unity/collectors.h
149src/Unity/previewwidgetmodel.h156src/Unity/previewwidgetmodel.h
150157
=== modified file 'src/CMakeLists.txt'
--- src/CMakeLists.txt 2015-06-17 06:56:58 +0000
+++ src/CMakeLists.txt 2016-06-10 15:06:19 +0000
@@ -1,14 +1,8 @@
1
2include_directories(
3 ${UBUNTU_LOCATION_SERVICE_INCLUDE_DIRS}
4)
5
6set(1set(
7 SCOPES_SHELL_DEPENDENCIES2 SCOPES_SHELL_DEPENDENCIES
8 ${SCOPESLIB_LDFLAGS}3 ${SCOPESLIB_LDFLAGS}
9 ${GSETTINGSQT_LDFLAGS}4 ${GSETTINGSQT_LDFLAGS}
10 ${U1DB_LDFLAGS}5 ${U1DB_LDFLAGS}
11 ${UBUNTU_LOCATION_SERVICE_LDFLAGS}
12 ${ONLINE_ACCOUNTS_CLIENT_LDFLAGS}6 ${ONLINE_ACCOUNTS_CLIENT_LDFLAGS}
13)7)
148
@@ -19,9 +13,9 @@
19 Gui13 Gui
20 Network14 Network
21 Qml15 Qml
16 Positioning
22)17)
2318
24add_subdirectory(Unity)19add_subdirectory(Unity)
25add_subdirectory(scope-harness)20add_subdirectory(scope-harness)
26add_subdirectory(python)21add_subdirectory(python)
27
2822
=== modified file 'src/Unity/CMakeLists.txt'
--- src/Unity/CMakeLists.txt 2016-03-10 15:54:33 +0000
+++ src/Unity/CMakeLists.txt 2016-06-10 15:06:19 +0000
@@ -29,7 +29,7 @@
29 valueslidervalues.cpp29 valueslidervalues.cpp
30 geoip.cpp30 geoip.cpp
31 localization.h31 localization.h
32 locationservice.cpp32 locationaccesshelper.cpp
33 overviewcategories.cpp33 overviewcategories.cpp
34 overviewresults.cpp34 overviewresults.cpp
35 overviewscope.cpp35 overviewscope.cpp
3636
=== added file 'src/Unity/locationaccesshelper.cpp'
--- src/Unity/locationaccesshelper.cpp 1970-01-01 00:00:00 +0000
+++ src/Unity/locationaccesshelper.cpp 2016-06-10 15:06:19 +0000
@@ -0,0 +1,105 @@
1/*
2 * Copyright (C) 2016 Canonical, Ltd.
3 *
4 * Authors:
5 * Pawel Stolowski <pawel.stolowski@canonical.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 3.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "locationaccesshelper.h"
21#include <QDir>
22#include <QFile>
23#include <QStandardPaths>
24#include <QDebug>
25
26namespace scopes_ng
27{
28
29const QString LocationAccessHelper::scopesLocationDotFile = ".scopesLocationPrompt";
30
31LocationAccessHelper::LocationAccessHelper(QObject *parent) :
32 QObject(parent),
33 m_dotFileExists(false),
34 m_denied(true)
35{
36}
37
38void LocationAccessHelper::init()
39{
40 auto const path = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
41 QDir locationPromptFile(path);
42 m_dotFileExists = locationPromptFile.exists(scopesLocationDotFile);
43
44 if (m_dotFileExists) {
45 // dot file exists, it means user was already prompted for location so we can
46 // safely request location on startup without risking immediate trusted prompt.
47 Q_EMIT requestInitialLocation();
48 }
49}
50
51bool LocationAccessHelper::trustedPromptWasShown() const
52{
53 return m_dotFileExists;
54}
55
56bool LocationAccessHelper::isLocationAccessDenied() const
57{
58 return m_denied;
59}
60
61void LocationAccessHelper::geoIpLookupFinished()
62{
63 qDebug() << "LocationAccessHelper::geoIpLookupFinished";
64 // This signal is not interesting at the moment. If, however we need to refresh scopes on location update,
65 // then it should be re-emited (forwarded) here and in positonChanged() below.
66}
67
68void LocationAccessHelper::positionChanged()
69{
70 if (m_denied) {
71 m_denied = false;
72 Q_EMIT accessChanged();
73 }
74
75 if (!m_dotFileExists) {
76 createLocationPromptFile();
77 }
78}
79
80void LocationAccessHelper::accessDenied()
81{
82 qDebug() << "LocationAccessHelper::accessDenied";
83 if (!m_denied) {
84 m_denied = true;
85 Q_EMIT accessChanged();
86 }
87
88 if (!m_dotFileExists) {
89 createLocationPromptFile();
90 }
91}
92
93void LocationAccessHelper::createLocationPromptFile()
94{
95 auto const path = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + "/" + scopesLocationDotFile;
96 QFile locationPromptFile(path);
97 if (locationPromptFile.open(QIODevice::WriteOnly)) {
98 qDebug() << "Creating" << locationPromptFile.fileName();
99 m_dotFileExists = true;
100 } else {
101 qWarning() << "Failed to create" << locationPromptFile.fileName();
102 }
103}
104
105}
0106
=== added file 'src/Unity/locationaccesshelper.h'
--- src/Unity/locationaccesshelper.h 1970-01-01 00:00:00 +0000
+++ src/Unity/locationaccesshelper.h 2016-06-10 15:06:19 +0000
@@ -0,0 +1,56 @@
1/*
2 * Copyright (C) 2016 Canonical, Ltd.
3 *
4 * Authors:
5 * Pawel Stolowski <pawel.stolowski@canonical.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 3.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#pragma once
21
22#include <QObject>
23
24namespace scopes_ng
25{
26
27class Q_DECL_EXPORT LocationAccessHelper: public QObject
28{
29 Q_OBJECT
30
31public:
32 LocationAccessHelper(QObject *parent = nullptr);
33 void init();
34
35 bool trustedPromptWasShown() const;
36 bool isLocationAccessDenied() const;
37
38public Q_SLOTS:
39 void accessDenied();
40 void positionChanged();
41 void geoIpLookupFinished();
42
43Q_SIGNALS:
44 void accessChanged();
45 void requestInitialLocation();
46
47private:
48 void createLocationPromptFile();
49
50 bool m_dotFileExists;
51 bool m_denied;
52
53 static const QString scopesLocationDotFile;
54};
55
56}
057
=== removed file 'src/Unity/locationservice.cpp'
--- src/Unity/locationservice.cpp 2014-07-15 08:37:40 +0000
+++ src/Unity/locationservice.cpp 1970-01-01 00:00:00 +0000
@@ -1,26 +0,0 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Pete Woods <pete.woods@canonical.com>
18 */
19
20#include <locationservice.h>
21
22using namespace scopes_ng;
23
24LocationService::LocationService() {
25
26}
270
=== removed file 'src/Unity/locationservice.h'
--- src/Unity/locationservice.h 2015-02-26 18:02:32 +0000
+++ src/Unity/locationservice.h 1970-01-01 00:00:00 +0000
@@ -1,72 +0,0 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Pete Woods <pete.woods@canonical.com>
18 */
19
20#ifndef LOCATIONSERVICE_H
21#define LOCATIONSERVICE_H
22
23#include <QObject>
24
25#include <com/ubuntu/location/heading.h>
26#include <com/ubuntu/location/position.h>
27#include <com/ubuntu/location/update.h>
28#include <com/ubuntu/location/velocity.h>
29
30#include <unity/scopes/Location.h>
31
32namespace scopes_ng
33{
34
35class Q_DECL_EXPORT LocationService : public QObject
36{
37 Q_OBJECT
38
39 Q_PROPERTY(unity::scopes::Location location READ location NOTIFY locationChanged)
40
41 Q_PROPERTY(bool active READ isActive NOTIFY activeChanged)
42
43public:
44 typedef QSharedPointer<LocationService> Ptr;
45
46 class Token : public QObject
47 {
48 };
49
50 LocationService();
51
52 virtual ~LocationService() = default;
53
54 virtual unity::scopes::Location location() const = 0;
55
56 virtual bool hasLocation() const = 0;
57
58 virtual bool isActive() const = 0;
59
60public Q_SLOTS:
61 virtual QSharedPointer<Token> activate() = 0;
62
63Q_SIGNALS:
64 void locationChanged();
65
66 void activeChanged();
67
68};
69
70} // namespace scopes_ng
71
72#endif /* LOCATIONSERVICE_H_ */
730
=== modified file 'src/Unity/overviewscope.cpp'
--- src/Unity/overviewscope.cpp 2016-02-04 15:19:37 +0000
+++ src/Unity/overviewscope.cpp 2016-06-10 15:06:19 +0000
@@ -145,7 +145,7 @@
145 categories->updateOtherScopes(otherScopes, scopeIdToName);145 categories->updateOtherScopes(otherScopes, scopeIdToName);
146}146}
147147
148void OverviewScope::dispatchSearch()148void OverviewScope::dispatchSearch(bool)
149{149{
150 qWarning() << "Search is not implemented for Manage Dash";150 qWarning() << "Search is not implemented for Manage Dash";
151}151}
152152
=== modified file 'src/Unity/overviewscope.h'
--- src/Unity/overviewscope.h 2016-02-04 15:19:37 +0000
+++ src/Unity/overviewscope.h 2016-06-10 15:06:19 +0000
@@ -37,7 +37,7 @@
37 /* getters */37 /* getters */
38 QString id() const override;38 QString id() const override;
3939
40 void dispatchSearch() override;40 void dispatchSearch(bool) override;
41 void setActive(const bool) override;41 void setActive(const bool) override;
4242
43 void updateFavorites(const QStringList& favorites);43 void updateFavorites(const QStringList& favorites);
4444
=== modified file 'src/Unity/scope.cpp'
--- src/Unity/scope.cpp 2016-05-25 08:16:09 +0000
+++ src/Unity/scope.cpp 2016-06-10 15:06:19 +0000
@@ -24,8 +24,8 @@
24// local24// local
25#include "categories.h"25#include "categories.h"
26#include "collectors.h"26#include "collectors.h"
27#include "locationaccesshelper.h"
27#include "previewmodel.h"28#include "previewmodel.h"
28#include "locationservice.h"
29#include "utils.h"29#include "utils.h"
30#include "scopes.h"30#include "scopes.h"
31#include "settingsmodel.h"31#include "settingsmodel.h"
@@ -77,7 +77,8 @@
7777
78Scope::Ptr Scope::newInstance(scopes_ng::Scopes* parent)78Scope::Ptr Scope::newInstance(scopes_ng::Scopes* parent)
79{79{
80 return Scope::Ptr(new Scope(parent), &QObject::deleteLater);80 auto scope = Scope::Ptr(new Scope(parent), &QObject::deleteLater);
81 return scope;
81}82}
8283
83Scope::Scope(scopes_ng::Scopes* parent) :84Scope::Scope(scopes_ng::Scopes* parent) :
@@ -696,7 +697,7 @@
696 m_filterState = filterState;697 m_filterState = filterState;
697}698}
698699
699void Scope::dispatchSearch()700void Scope::dispatchSearch(bool programmaticSearch)
700{701{
701 m_initialQueryDone = true;702 m_initialQueryDone = true;
702703
@@ -736,6 +737,20 @@
736 // (i.e. while the loading bar is visible).737 // (i.e. while the loading bar is visible).
737 update_child_scopes();738 update_child_scopes();
738739
740 // handle the case where single scope is refreshed multiple times without switching
741 // to another scope, which would never trigger location prompt.
742 if (m_scopeMetadata && m_scopeMetadata->location_data_needed() && !m_locationToken)
743 {
744 if (m_isActive)
745 {
746 Q_ASSERT(m_scopesInstance);
747
748 if ((!programmaticSearch) || m_scopesInstance->locationAccessHelper()->trustedPromptWasShown()) {
749 m_locationToken = m_locationService->activate();
750 }
751 }
752 }
753
739 if (m_proxy) {754 if (m_proxy) {
740 scopes::SearchMetadata meta(m_cardinality, QLocale::system().name().toStdString(), m_formFactor.toStdString());755 scopes::SearchMetadata meta(m_cardinality, QLocale::system().name().toStdString(), m_formFactor.toStdString());
741 auto const userAgent = m_scopesInstance->userAgentString();756 auto const userAgent = m_scopesInstance->userAgentString();
@@ -764,6 +779,7 @@
764779
765 scopes::SearchListenerBase::SPtr listener(new SearchResultReceiver(this));780 scopes::SearchListenerBase::SPtr listener(new SearchResultReceiver(this));
766 m_searchController->setListener(listener);781 m_searchController->setListener(listener);
782
767 try {783 try {
768 qDebug() << "Dispatching search:" << id() << m_searchQuery << m_currentNavigationId;784 qDebug() << "Dispatching search:" << id() << m_searchQuery << m_currentNavigationId;
769 scopes::QueryCtrlProxy controller = m_queryUserData ?785 scopes::QueryCtrlProxy controller = m_queryUserData ?
@@ -792,6 +808,11 @@
792 m_customizations = converted.toMap();808 m_customizations = converted.toMap();
793 Q_EMIT customizationsChanged();809 Q_EMIT customizationsChanged();
794810
811 createSettingsModel();
812}
813
814void Scope::createSettingsModel()
815{
795 try816 try
796 {817 {
797 scopes::Variant settings_definitions;818 scopes::Variant settings_definitions;
@@ -806,10 +827,20 @@
806 shareDir = QDir::home().filePath(QStringLiteral(".config/unity-scopes"));827 shareDir = QDir::home().filePath(QStringLiteral(".config/unity-scopes"));
807 }828 }
808829
830 Q_ASSERT(m_scopesInstance);
831
809 m_settingsModel.reset(832 m_settingsModel.reset(
810 new SettingsModel(shareDir, id(),833 new SettingsModel(shareDir, id(),
811 scopeVariantToQVariant(settings_definitions), this));834 scopeVariantToQVariant(settings_definitions),
835 !m_scopesInstance->locationAccessHelper()->isLocationAccessDenied(),
836 this));
837
812 QObject::connect(m_settingsModel.data(), &SettingsModel::settingsChanged, this, &Scope::invalidateResults);838 QObject::connect(m_settingsModel.data(), &SettingsModel::settingsChanged, this, &Scope::invalidateResults);
839
840 // If the scope needs location, then changes to global location access need to be monitored.
841 if (m_scopeMetadata->location_data_needed()) {
842 QObject::connect(m_scopesInstance->locationAccessHelper().data(), &LocationAccessHelper::accessChanged, this, &Scope::locationAccessChanged);
843 }
813 }844 }
814 catch (unity::scopes::NotFoundException&)845 catch (unity::scopes::NotFoundException&)
815 {846 {
@@ -906,6 +937,16 @@
906 return m_settingsModel.data();937 return m_settingsModel.data();
907}938}
908939
940void Scope::locationAccessChanged()
941{
942 qDebug() << "Location access changed, recreating settings model for scope" << id();
943 createSettingsModel();
944
945 // Force child scopes refresh
946 m_childScopesDirty = true;
947 update_child_scopes();
948}
949
909bool Scope::require_child_scopes_refresh() const950bool Scope::require_child_scopes_refresh() const
910{951{
911 if (m_settingsModel && m_scopesInstance)952 if (m_settingsModel && m_scopesInstance)
@@ -1116,11 +1157,7 @@
11161157
1117 if (m_scopeMetadata && m_scopeMetadata->location_data_needed())1158 if (m_scopeMetadata && m_scopeMetadata->location_data_needed())
1118 {1159 {
1119 if (m_isActive)1160 if (!m_isActive)
1120 {
1121 m_locationToken = m_locationService->activate();
1122 }
1123 else
1124 {1161 {
1125 m_locationToken.reset();1162 m_locationToken.reset();
1126 }1163 }
11271164
=== modified file 'src/Unity/scope.h'
--- src/Unity/scope.h 2016-05-06 07:32:13 +0000
+++ src/Unity/scope.h 2016-06-10 15:06:19 +0000
@@ -43,7 +43,8 @@
43#include "collectors.h"43#include "collectors.h"
44#include "departmentnode.h"44#include "departmentnode.h"
45#include "department.h"45#include "department.h"
46#include "locationservice.h"46#include "ubuntulocationservice.h"
47#include "locationaccesshelper.h"
4748
48namespace scopes_ng49namespace scopes_ng
49{50{
@@ -51,7 +52,6 @@
51class Categories;52class Categories;
52class PushEvent;53class PushEvent;
53class PreviewModel;54class PreviewModel;
54class LocationService;
55class SettingsModel;55class SettingsModel;
56class Scopes;56class Scopes;
5757
@@ -185,7 +185,7 @@
185public Q_SLOTS:185public Q_SLOTS:
186 void invalidateChildScopes();186 void invalidateChildScopes();
187 void invalidateResults();187 void invalidateResults();
188 virtual void dispatchSearch();188 virtual void dispatchSearch(bool programmaticSearch = false);
189 void setSearchInProgress(bool searchInProgress);189 void setSearchInProgress(bool searchInProgress);
190 void setActivationInProgress(bool activationInProgress);190 void setActivationInProgress(bool activationInProgress);
191191
@@ -200,6 +200,7 @@
200 void flushUpdates(bool finalize = false);200 void flushUpdates(bool finalize = false);
201 void metadataRefreshed();201 void metadataRefreshed();
202 void departmentModelDestroyed(QObject* obj);202 void departmentModelDestroyed(QObject* obj);
203 void locationAccessChanged();
203 void filterStateChanged();204 void filterStateChanged();
204 void previewModelDestroyed(QObject *obj);205 void previewModelDestroyed(QObject *obj);
205206
@@ -208,6 +209,7 @@
208209
209 void setStatus(unity::shell::scopes::ScopeInterface::Status status);210 void setStatus(unity::shell::scopes::ScopeInterface::Status status);
210 void invalidateLastSearch();211 void invalidateLastSearch();
212 void createSettingsModel();
211213
212 unity::scopes::ScopeProxy proxy() const;214 unity::scopes::ScopeProxy proxy() const;
213215
@@ -278,8 +280,8 @@
278 QMultiMap<QString, Department*> m_departmentModels;280 QMultiMap<QString, Department*> m_departmentModels;
279 QMap<Department*, QString> m_inverseDepartments;281 QMap<Department*, QString> m_inverseDepartments;
280 QMetaObject::Connection m_metadataConnection;282 QMetaObject::Connection m_metadataConnection;
281 QSharedPointer<LocationService> m_locationService;283 QSharedPointer<UbuntuLocationService> m_locationService;
282 QSharedPointer<LocationService::Token> m_locationToken;284 QSharedPointer<UbuntuLocationService::Token> m_locationToken;
283 QNetworkConfigurationManager m_network_manager;285 QNetworkConfigurationManager m_network_manager;
284 QList<PreviewModel*> m_previewModels;286 QList<PreviewModel*> m_previewModels;
285};287};
286288
=== modified file 'src/Unity/scopes.cpp'
--- src/Unity/scopes.cpp 2016-05-05 09:41:13 +0000
+++ src/Unity/scopes.cpp 2016-06-10 15:06:19 +0000
@@ -107,6 +107,7 @@
107 , m_listThread(nullptr)107 , m_listThread(nullptr)
108 , m_loaded(false)108 , m_loaded(false)
109 , m_prepopulateFirstScope(true)109 , m_prepopulateFirstScope(true)
110 , m_locationAccessHelper(new LocationAccessHelper(nullptr))
110 , m_priv(new Priv())111 , m_priv(new Priv())
111{112{
112 QByteArray noFav = qgetenv("UNITY_SCOPES_NO_FAVORITES");113 QByteArray noFav = qgetenv("UNITY_SCOPES_NO_FAVORITES");
@@ -136,6 +137,11 @@
136 connect(&m_registryRefreshTimer, SIGNAL(timeout()), this, SLOT(scopeRegistryChanged()));137 connect(&m_registryRefreshTimer, SIGNAL(timeout()), this, SLOT(scopeRegistryChanged()));
137138
138 m_locationService.reset(new UbuntuLocationService());139 m_locationService.reset(new UbuntuLocationService());
140 QObject::connect(m_locationAccessHelper.data(), &LocationAccessHelper::requestInitialLocation, m_locationService.data(), &UbuntuLocationService::requestInitialLocation);
141 QObject::connect(m_locationService.data(), &UbuntuLocationService::accessDenied, m_locationAccessHelper.data(), &LocationAccessHelper::accessDenied);
142 QObject::connect(m_locationService.data(), &UbuntuLocationService::locationChanged, m_locationAccessHelper.data(), &LocationAccessHelper::positionChanged);
143 QObject::connect(m_locationService.data(), &UbuntuLocationService::geoIpLookupFinished, m_locationAccessHelper.data(), &LocationAccessHelper::geoIpLookupFinished);
144 m_locationAccessHelper->init();
139145
140 createUserAgentString();146 createUserAgentString();
141147
@@ -162,6 +168,11 @@
162 m_scopesToDelete.clear();168 m_scopesToDelete.clear();
163}169}
164170
171QSharedPointer<LocationAccessHelper> Scopes::locationAccessHelper() const
172{
173 return m_locationAccessHelper;
174}
175
165int Scopes::rowCount(const QModelIndex& parent) const176int Scopes::rowCount(const QModelIndex& parent) const
166{177{
167 Q_UNUSED(parent)178 Q_UNUSED(parent)
@@ -330,9 +341,15 @@
330 }341 }
331 else342 else
332 {343 {
344 qDebug() << "Waiting for initial location update";
345
333 // Otherwise we have to wait for location data346 // Otherwise we have to wait for location data
334 // Either the the location data needs to change, or the timeout happens347 // Either the the location data needs to change, or the timeout happens
335 connect(m_locationService.data(), &LocationService::locationChanged,348 connect(m_locationService.data(), &UbuntuLocationService::locationChanged,
349 this, &Scopes::completeDiscoveryFinished);
350 connect(m_locationService.data(), &UbuntuLocationService::accessDenied,
351 this, &Scopes::completeDiscoveryFinished);
352 connect(m_locationService.data(), &UbuntuLocationService::locationTimeout,
336 this, &Scopes::completeDiscoveryFinished);353 this, &Scopes::completeDiscoveryFinished);
337 connect(&m_startupQueryTimeout, &QTimer::timeout, this,354 connect(&m_startupQueryTimeout, &QTimer::timeout, this,
338 &Scopes::completeDiscoveryFinished);355 &Scopes::completeDiscoveryFinished);
@@ -344,11 +361,16 @@
344361
345void Scopes::completeDiscoveryFinished()362void Scopes::completeDiscoveryFinished()
346{363{
364 qDebug() << "Scopes discovery completed";
347 // Kill off everything that could potentially trigger the startup queries365 // Kill off everything that could potentially trigger the startup queries
348 m_startupQueryTimeout.stop();366 m_startupQueryTimeout.stop();
349 disconnect(&m_startupQueryTimeout, &QTimer::timeout, this,367 disconnect(&m_startupQueryTimeout, &QTimer::timeout, this,
350 &Scopes::completeDiscoveryFinished);368 &Scopes::completeDiscoveryFinished);
351 disconnect(m_locationService.data(), &LocationService::locationChanged,369 disconnect(m_locationService.data(), &UbuntuLocationService::locationChanged,
370 this, &Scopes::completeDiscoveryFinished);
371 disconnect(m_locationService.data(), &UbuntuLocationService::accessDenied,
372 this, &Scopes::completeDiscoveryFinished);
373 disconnect(m_locationService.data(), &UbuntuLocationService::locationTimeout,
352 this, &Scopes::completeDiscoveryFinished);374 this, &Scopes::completeDiscoveryFinished);
353375
354 processFavoriteScopes();376 processFavoriteScopes();
@@ -376,7 +398,7 @@
376 qDebug() << "Pre-populating first scope";398 qDebug() << "Pre-populating first scope";
377 scope->setSearchQuery(QLatin1String(""));399 scope->setSearchQuery(QLatin1String(""));
378 // must dispatch search explicitly since setSearchQuery will not do that for inactive scope400 // must dispatch search explicitly since setSearchQuery will not do that for inactive scope
379 scope->dispatchSearch();401 scope->dispatchSearch(true);
380 }402 }
381 }403 }
382}404}
@@ -393,7 +415,7 @@
393 qDebug() << "Pre-populating scope" << scope->id();415 qDebug() << "Pre-populating scope" << scope->id();
394 scope->setSearchQuery(QLatin1String(""));416 scope->setSearchQuery(QLatin1String(""));
395 // must dispatch search explicitly since setSearchQuery will not do that for inactive scope417 // must dispatch search explicitly since setSearchQuery will not do that for inactive scope
396 scope->dispatchSearch();418 scope->dispatchSearch(true);
397 }419 }
398 }420 }
399 break;421 break;
@@ -791,7 +813,7 @@
791 return m_loaded;813 return m_loaded;
792}814}
793815
794LocationService::Ptr Scopes::locationService() const816UbuntuLocationService::Ptr Scopes::locationService() const
795{817{
796 return m_locationService;818 return m_locationService;
797}819}
798820
=== modified file 'src/Unity/scopes.h'
--- src/Unity/scopes.h 2016-04-18 10:10:25 +0000
+++ src/Unity/scopes.h 2016-06-10 15:06:19 +0000
@@ -22,6 +22,7 @@
2222
23#include <unity/shell/scopes/ScopesInterface.h>23#include <unity/shell/scopes/ScopesInterface.h>
24#include "scope.h"24#include "scope.h"
25#include "locationaccesshelper.h"
2526
26// Qt27// Qt
27#include <QList>28#include <QList>
@@ -43,7 +44,8 @@
43namespace scopes_ng44namespace scopes_ng
44{45{
4546
46class LocationService;47class UbuntuLocationService;
48class LocationAccessHelper;
47class Scope;49class Scope;
48class OverviewScope;50class OverviewScope;
4951
@@ -75,12 +77,13 @@
75 unity::shell::scopes::ScopeInterface* overviewScope() const override;77 unity::shell::scopes::ScopeInterface* overviewScope() const override;
76 Scope::Ptr overviewScopeSPtr() const;78 Scope::Ptr overviewScopeSPtr() const;
7779
78 QSharedPointer<LocationService> locationService() const;80 QSharedPointer<UbuntuLocationService> locationService() const;
79 QString userAgentString() const;81 QString userAgentString() const;
8082
81 Scope::Ptr findTempScope(QString const& id) const;83 Scope::Ptr findTempScope(QString const& id) const;
82 void addTempScope(Scope::Ptr const& scope);84 void addTempScope(Scope::Ptr const& scope);
83 Q_INVOKABLE void closeScope(unity::shell::scopes::ScopeInterface* scope) override;85 Q_INVOKABLE void closeScope(unity::shell::scopes::ScopeInterface* scope) override;
86 QSharedPointer<LocationAccessHelper> locationAccessHelper() const;
8487
85Q_SIGNALS:88Q_SIGNALS:
86 void metadataRefreshed();89 void metadataRefreshed();
@@ -124,10 +127,11 @@
124 bool m_loaded;127 bool m_loaded;
125 bool m_prepopulateFirstScope;128 bool m_prepopulateFirstScope;
126129
127 QSharedPointer<LocationService> m_locationService;130 QSharedPointer<UbuntuLocationService> m_locationService;
128 QTimer m_startupQueryTimeout;131 QTimer m_startupQueryTimeout;
129 QTimer m_scopesToDeleteTimer;132 QTimer m_scopesToDeleteTimer;
130 QTimer m_registryRefreshTimer;133 QTimer m_registryRefreshTimer;
134 QSharedPointer<LocationAccessHelper> m_locationAccessHelper;
131135
132 unity::scopes::Runtime::SPtr m_scopesRuntime;136 unity::scopes::Runtime::SPtr m_scopesRuntime;
133 QMap<QString, Scope::Ptr> m_tempScopes;137 QMap<QString, Scope::Ptr> m_tempScopes;
134138
=== modified file 'src/Unity/settingsmodel.cpp'
--- src/Unity/settingsmodel.cpp 2016-05-19 07:18:18 +0000
+++ src/Unity/settingsmodel.cpp 2016-06-10 15:06:19 +0000
@@ -74,7 +74,8 @@
74} // namespace74} // namespace
7575
76SettingsModel::SettingsModel(const QDir& configDir, const QString& scopeId,76SettingsModel::SettingsModel(const QDir& configDir, const QString& scopeId,
77 const QVariant& settingsDefinitions, QObject* parent,77 const QVariant& settingsDefinitions, bool isLocationGloballyEnabled,
78 QObject* parent,
78 int settingsTimeout)79 int settingsTimeout)
79 : SettingsModelInterface(parent), m_scopeId(scopeId), m_settingsTimeout(settingsTimeout),80 : SettingsModelInterface(parent), m_scopeId(scopeId), m_settingsTimeout(settingsTimeout),
80 m_requireChildScopesRefresh(false)81 m_requireChildScopesRefresh(false)
@@ -98,6 +99,12 @@
98 QVariantMap data = it.toMap();99 QVariantMap data = it.toMap();
99 QString id = data[QStringLiteral("id")].toString();100 QString id = data[QStringLiteral("id")].toString();
100 QString displayName = data[QStringLiteral("displayName")].toString();101 QString displayName = data[QStringLiteral("displayName")].toString();
102
103 if (id == "internal.location" && !isLocationGloballyEnabled) {
104 qDebug() << "Location setting ignored, waiting for global location access to be enabled first";
105 continue;
106 }
107
101 QVariantMap properties;108 QVariantMap properties;
102 QVariant defaultValue;109 QVariant defaultValue;
103 if (data.contains(QStringLiteral("displayValues")))110 if (data.contains(QStringLiteral("displayValues")))
104111
=== modified file 'src/Unity/settingsmodel.h'
--- src/Unity/settingsmodel.h 2016-05-19 07:06:55 +0000
+++ src/Unity/settingsmodel.h 2016-06-10 15:06:19 +0000
@@ -65,7 +65,8 @@
6565
66public:66public:
67 explicit SettingsModel(const QDir& configDir, const QString& scopeId,67 explicit SettingsModel(const QDir& configDir, const QString& scopeId,
68 const QVariant& settingsDefinitions, QObject* parent = 0,68 const QVariant& settingsDefinitions, bool isLocationGloballyEnabled = true,
69 QObject* parent = 0,
69 int settingsTimeout = 300);70 int settingsTimeout = 300);
7071
71 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const72 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const
7273
=== modified file 'src/Unity/ubuntulocationservice.cpp'
--- src/Unity/ubuntulocationservice.cpp 2015-09-08 12:32:05 +0000
+++ src/Unity/ubuntulocationservice.cpp 2016-06-10 15:06:19 +0000
@@ -20,27 +20,10 @@
20#include "ubuntulocationservice.h"20#include "ubuntulocationservice.h"
2121
22#include <QDebug>22#include <QDebug>
23#include <QMutexLocker>
24#include <QRunnable>
25#include <QThreadPool>
26#include <QTimer>
27
28#include <com/ubuntu/location/service/stub.h>
29
30#include <core/dbus/resolver.h>
31#include <core/dbus/asio/executor.h>
32
33#include <memory>
3423
35using namespace std;24using namespace std;
36using namespace std::placeholders;
37using namespace scopes_ng;25using namespace scopes_ng;
3826
39namespace cul = com::ubuntu::location;
40namespace culs = com::ubuntu::location::service;
41namespace culss = com::ubuntu::location::service::session;
42namespace culu = com::ubuntu::location::units;
43namespace dbus = core::dbus;
44namespace scopes = unity::scopes;27namespace scopes = unity::scopes;
4528
46namespace29namespace
@@ -55,28 +38,9 @@
55 * Re-do the GeoIP call every 60 seconds38 * Re-do the GeoIP call every 60 seconds
56 */39 */
57 static const int GEOIP_INTERVAL = 60000;40 static const int GEOIP_INTERVAL = 60000;
58
59 class DBusThread : public QThread
60 {
61
62 public:
63 DBusThread(const dbus::Bus::Ptr& bus) :
64 m_bus(bus)
65 {
66 }
67
68 protected:
69 void run() override
70 {
71 m_bus->run();
72 }
73
74 dbus::Bus::Ptr m_bus;
75 };
76
77}41}
7842
79class UbuntuLocationService::TokenImpl: public LocationService::Token43class UbuntuLocationService::TokenImpl: public UbuntuLocationService::Token
80{44{
81 Q_OBJECT45 Q_OBJECT
8246
@@ -96,220 +60,121 @@
96 void destroyed();60 void destroyed();
97};61};
9862
99class UbuntuLocationService::Priv : public QObject63UbuntuLocationService::UbuntuLocationService(const GeoIp::Ptr& geoIp)
100{64 : m_geoIp(geoIp)
101Q_OBJECT65{
102
103public:
104 Priv() :
105 m_lastLocationMutex(QMutex::Recursive), m_resultMutex(
106 QMutex::Recursive)
107 {
108 }
109
110 void init(const GeoIp::Ptr& geoIp)
111 {
112 m_geoIp = geoIp;
113 m_geoIp->whollyMoveThread(thread());
114
115 m_deactivateTimer.moveToThread(thread());
116 m_deactivateTimer.setInterval(DEACTIVATE_INTERVAL);
117 m_deactivateTimer.setSingleShot(true);
118 m_deactivateTimer.setTimerType(Qt::VeryCoarseTimer);
119
120 m_geoipTimer.moveToThread(thread());
121 m_geoipTimer.setInterval(GEOIP_INTERVAL);
122 m_geoipTimer.setTimerType(Qt::CoarseTimer);
123
124 QMetaObject::invokeMethod(m_geoIp.data(), "start", Qt::QueuedConnection);
125
126 try
127 {
128 m_bus = make_shared<dbus::Bus>(dbus::WellKnownBus::system);
129 m_bus->install_executor(dbus::asio::make_executor(m_bus));
130
131 m_dbusThread.reset(new DBusThread(m_bus));
132 m_dbusThread->start();
133
134 m_locationService = dbus::resolve_service_on_bus<culs::Interface,
135 culs::Stub>(m_bus);
136 }
137 catch (exception& e)
138 {
139 qWarning() << e.what();
140 }
141
142 // Wire up the deactivate timer
143 connect(&m_deactivateTimer, &QTimer::timeout, this, &Priv::update, Qt::QueuedConnection);
144
145 // Wire up the network request finished timer
146 connect(m_geoIp.data(), &GeoIp::finished, this, &Priv::requestFinished, Qt::QueuedConnection);
147
148 // Wire up the GeoIP repeat timer
149 connect(&m_geoipTimer, &QTimer::timeout, m_geoIp.data(), &GeoIp::start, Qt::QueuedConnection);
150 }
151
152 ~Priv()
153 {
154 if (m_bus)
155 {
156 m_bus->stop();
157 }
158
159 if (m_dbusThread && m_dbusThread->isRunning())
160 {
161 m_dbusThread->wait();
162 }
163 }
164
165Q_SIGNALS:
166 void locationChanged();
167
168public Q_SLOTS:
169 void update()
170 {
171 if (!m_locationService)
172 {
173 qWarning() << "Location service not available";
174 return;
175 }
176
177 if (m_activationCount > 0)
178 {
179 // Update the GeoIp data again
180 m_geoIp->start();
181 }
182
183 try
184 {
185 if (!m_session)
186 {
187 m_session = m_locationService->create_session_for_criteria(
188 cul::Criteria());
189
190 m_session->updates().position.changed().connect(
191 bind(&UbuntuLocationService::Priv::positionChanged,
192 this, _1));
193 }
194
195 if (m_activationCount > 0
196 && m_session->updates().position_status
197 == culss::Interface::Updates::Status::disabled)
198 {
199 qDebug() << "Enabling location updates";
200 m_session->updates().position_status =
201 culss::Interface::Updates::Status::enabled;
202 m_geoipTimer.start();
203 }
204 else if (m_activationCount == 0
205 && m_session->updates().position_status
206 == culss::Interface::Updates::Status::enabled)
207 {
208 qDebug() << "Disabling location updates";
209 m_session->updates().position_status =
210 culss::Interface::Updates::Status::disabled;
211 m_geoipTimer.stop();
212 }
213 }
214 catch (exception& e)
215 {
216 qWarning() << e.what();
217 }
218 }
219
220 void positionChanged(const cul::Update<cul::Position>& newPosition)
221 {
222 QMutexLocker lock(&m_lastLocationMutex);
223
224 m_locationUpdatedAtLeastOnce = true;
225 m_lastLocation = newPosition.value;
226 Q_EMIT locationChanged();
227 }
228
229 void requestFinished(const GeoIp::Result& result)
230 {
231 QMutexLocker lock(&m_resultMutex);
232 m_result = result;
233 Q_EMIT locationChanged();
234 }
235
236 void activate()
237 {
238 ++m_activationCount;
239 m_deactivateTimer.stop();
240 update();
241 }
242
243 void deactivate()
244 {
245 --m_activationCount;
246 if (m_activationCount < 0)
247 {
248 m_activationCount = 0;
249 qWarning() << "Location service refcount error";
250 }
251 m_deactivateTimer.start();
252 }
253
254public:
255 dbus::Bus::Ptr m_bus;
256
257 culs::Stub::Ptr m_locationService;
258
259 culss::Interface::Ptr m_session;
260
261 cul::Position m_lastLocation;
262
263 QMutex m_lastLocationMutex;
264
265 bool m_locationUpdatedAtLeastOnce = false;
266
267 int m_activationCount = 0;
268
269 QTimer m_geoipTimer;
270
271 QTimer m_deactivateTimer;
272
273 GeoIp::Ptr m_geoIp;
274
275 QSharedPointer<QThread> m_dbusThread;
276
277 QMutex m_resultMutex;
278
279 GeoIp::Result m_result;
280};
281
282UbuntuLocationService::UbuntuLocationService(const GeoIp::Ptr& geoIp) :
283 p(new Priv())
284{
285 p->moveToThread(&m_thread);
286
287 // If the location service is disabled66 // If the location service is disabled
288 if (qEnvironmentVariableIsSet("UNITY_SCOPES_NO_LOCATION"))67 if (qEnvironmentVariableIsSet("UNITY_SCOPES_NO_LOCATION"))
289 {68 {
290 return;69 return;
291 }70 }
29271
293 p->init(geoIp);72 m_deactivateTimer.setInterval(DEACTIVATE_INTERVAL);
73 m_deactivateTimer.setSingleShot(true);
74 m_deactivateTimer.setTimerType(Qt::VeryCoarseTimer);
75
76 m_geoipTimer.setInterval(GEOIP_INTERVAL);
77 m_geoipTimer.setTimerType(Qt::CoarseTimer);
78
79 m_locationSource = QGeoPositionInfoSource::createDefaultSource(this);
80 connect(m_locationSource, &QGeoPositionInfoSource::positionUpdated, this, &UbuntuLocationService::positionChanged);
81 connect(m_locationSource, &QGeoPositionInfoSource::updateTimeout, this, &UbuntuLocationService::onPositionUpdateTimeout);
82 connect(m_locationSource, SIGNAL(error(QGeoPositionInfoSource::Error)), this, SLOT(onError(QGeoPositionInfoSource::Error)));
83
84 // Wire up the deactivate timer
85 connect(&m_deactivateTimer, &QTimer::timeout, this, &UbuntuLocationService::update);
86
87 // Wire up the network request finished timer
88 connect(m_geoIp.data(), &GeoIp::finished, this, &UbuntuLocationService::requestFinished);
89
90 // Wire up the GeoIP repeat timer
91 connect(&m_geoipTimer, &QTimer::timeout, m_geoIp.data(), &GeoIp::start);
29492
295 // Connect to signals (which will be queued)93 // Connect to signals (which will be queued)
296 connect(p.data(), &Priv::locationChanged, this, &LocationService::locationChanged, Qt::QueuedConnection);94 connect(this, &UbuntuLocationService::enqueueActivate, this, &UbuntuLocationService::doActivate, Qt::QueuedConnection);
297 connect(this, &UbuntuLocationService::enqueueActivate, p.data(), &Priv::activate, Qt::QueuedConnection);95 connect(this, &UbuntuLocationService::enqueueDeactivate, this, &UbuntuLocationService::doDeactivate, Qt::QueuedConnection);
298 connect(this, &UbuntuLocationService::enqueueDeactivate, p.data(), &Priv::deactivate, Qt::QueuedConnection);96
29997 m_geoIp->start();
300 m_thread.start();98}
301}99
302100void UbuntuLocationService::doActivate()
303UbuntuLocationService::~UbuntuLocationService()101{
304{102 m_active = true;
305 p.reset();103 ++m_activationCount;
306104 m_deactivateTimer.stop();
307 m_thread.quit();105 update();
308106}
309 if (m_thread.isRunning())107
310 {108void UbuntuLocationService::doDeactivate()
311 m_thread.wait();109{
312 }110 --m_activationCount;
111 if (m_activationCount < 0)
112 {
113 m_activationCount = 0;
114 qWarning() << "Location service refcount error";
115 }
116 m_deactivateTimer.start();
117}
118
119void UbuntuLocationService::update()
120{
121 if (m_activationCount > 0)
122 {
123 // Update the GeoIp data again
124 m_geoIp->start();
125 }
126
127 try
128 {
129 if (m_activationCount > 0)
130 {
131 qDebug() << "Enabling location updates";
132 m_locationSource->startUpdates();
133 m_geoipTimer.start();
134 }
135 else
136 {
137 qDebug() << "Disabling location updates";
138 m_active = false;
139 m_locationSource->stopUpdates();
140 m_geoipTimer.stop();
141 }
142 }
143 catch (exception& e)
144 {
145 qWarning() << e.what();
146 }
147}
148
149void UbuntuLocationService::positionChanged(const QGeoPositionInfo& update)
150{
151 m_locationUpdatedAtLeastOnce = true;
152 m_lastLocation = update;
153 Q_EMIT locationChanged();
154}
155
156void UbuntuLocationService::onPositionUpdateTimeout()
157{
158 qWarning() << "Position update timeout";
159 Q_EMIT locationTimeout();
160}
161
162void UbuntuLocationService::onError(QGeoPositionInfoSource::Error positioningError)
163{
164 qWarning() << "Position update error:" << positioningError;
165 if (positioningError == QGeoPositionInfoSource::AccessError) {
166 qDebug() << "Postion update denied";
167 Q_EMIT accessDenied();
168 }
169}
170
171void UbuntuLocationService::requestFinished(const GeoIp::Result& result)
172{
173 qDebug() << "GeoIP request finished";
174 {
175 m_result = result;
176 }
177 Q_EMIT geoIpLookupFinished();
313}178}
314179
315scopes::Location UbuntuLocationService::location() const180scopes::Location UbuntuLocationService::location() const
@@ -318,8 +183,7 @@
318183
319 GeoIp::Result result;184 GeoIp::Result result;
320 {185 {
321 QMutexLocker lock(&p->m_resultMutex);186 result = m_result;
322 result = p->m_result;
323 }187 }
324188
325 if (result.valid)189 if (result.valid)
@@ -336,28 +200,21 @@
336 location.set_city(result.city.toStdString());200 location.set_city(result.city.toStdString());
337 }201 }
338202
339 QMutexLocker lock(&p->m_lastLocationMutex);
340 // We need to be active, and the location session must have updated at least once203 // We need to be active, and the location session must have updated at least once
341 if (isActive() && p->m_locationUpdatedAtLeastOnce)204 if (isActive() && m_locationUpdatedAtLeastOnce)
342 {205 {
343 cul::Position pos = p->m_lastLocation;206 location.set_latitude(m_lastLocation.coordinate().latitude());
344207 location.set_longitude(m_lastLocation.coordinate().longitude());
345 if (pos.accuracy.horizontal)208 location.set_altitude(m_lastLocation.coordinate().altitude());
346 {209
347 location.set_horizontal_accuracy(pos.accuracy.horizontal.get().value());210 if (m_lastLocation.hasAttribute(QGeoPositionInfo::HorizontalAccuracy))
348 }211 {
349 if (pos.accuracy.vertical)212 location.set_horizontal_accuracy(m_lastLocation.attribute(QGeoPositionInfo::HorizontalAccuracy));
350 {213 }
351 location.set_vertical_accuracy(pos.accuracy.vertical.get().value());214 if (m_lastLocation.hasAttribute(QGeoPositionInfo::VerticalAccuracy))
352 }215 {
353216 location.set_vertical_accuracy(m_lastLocation.attribute(QGeoPositionInfo::VerticalAccuracy));
354 if (pos.altitude)217 }
355 {
356 location.set_altitude(pos.altitude.get().value.value());
357 }
358
359 location.set_latitude(pos.latitude.value.value());
360 location.set_longitude(pos.longitude.value.value());
361 }218 }
362 else if (result.valid)219 else if (result.valid)
363 {220 {
@@ -376,18 +233,24 @@
376233
377bool UbuntuLocationService::isActive() const234bool UbuntuLocationService::isActive() const
378{235{
379 return p->m_session ? (p->m_session->updates().position_status ==236 return m_active;
380 culss::Interface::Updates::Status::enabled) : false;
381}237}
382238
383bool UbuntuLocationService::hasLocation() const239bool UbuntuLocationService::hasLocation() const
384{240{
385 return p->m_result.valid || p->m_locationUpdatedAtLeastOnce;241 return m_lastLocation.isValid() || m_locationUpdatedAtLeastOnce;
386}242}
387243
388QSharedPointer<LocationService::Token> UbuntuLocationService::activate()244QSharedPointer<UbuntuLocationService::Token> UbuntuLocationService::activate()
389{245{
390 return QSharedPointer<Token>(new TokenImpl(*this));246 return QSharedPointer<Token>(new TokenImpl(*this));
391}247}
392248
249void UbuntuLocationService::requestInitialLocation()
250{
251 qDebug() << "Requesting initial location update";
252 m_locationSource->requestUpdate();
253 m_geoipTimer.start();
254}
255
393#include "ubuntulocationservice.moc"256#include "ubuntulocationservice.moc"
394257
=== modified file 'src/Unity/ubuntulocationservice.h'
--- src/Unity/ubuntulocationservice.h 2015-09-08 12:32:05 +0000
+++ src/Unity/ubuntulocationservice.h 2016-06-10 15:06:19 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2014 Canonical, Ltd.2 * Copyright (C) 2014-2016 Canonical, Ltd.
3 *3 *
4 * This program is free software; you can redistribute it and/or modify4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by5 * it under the terms of the GNU General Public License as published by
@@ -15,50 +15,80 @@
15 *15 *
16 * Authors:16 * Authors:
17 * Pete Woods <pete.woods@canonical.com>17 * Pete Woods <pete.woods@canonical.com>
18 * Pawel Stolowski <pawel.stolowski@canonical.com>
18 */19 */
1920
20#ifndef UBUNTULOCATIONSERVICE_H21#ifndef UBUNTULOCATIONSERVICE_H
21#define UBUNTULOCATIONSERVICE_H22#define UBUNTULOCATIONSERVICE_H
2223
23#include "geoip.h"24#include "geoip.h"
24#include "locationservice.h"
2525
26#include <QObject>
26#include <QSharedPointer>27#include <QSharedPointer>
27#include <QThread>28#include <QTimer>
29#include <QGeoPositionInfo>
30#include <QGeoPositionInfoSource>
31#include <unity/scopes/Location.h>
2832
29namespace scopes_ng33namespace scopes_ng
30{34{
3135
32class Q_DECL_EXPORT UbuntuLocationService : public LocationService36class Q_DECL_EXPORT UbuntuLocationService: public QObject
33{37{
34 Q_OBJECT38 Q_OBJECT
39 Q_PROPERTY(unity::scopes::Location location READ location NOTIFY locationChanged)
40 Q_PROPERTY(bool active READ isActive NOTIFY activeChanged)
3541
36public:42public:
43 typedef QSharedPointer<UbuntuLocationService> Ptr;
44 class Token : public QObject
45 {
46 };
47
37 class TokenImpl;48 class TokenImpl;
3849
39 UbuntuLocationService(const GeoIp::Ptr& geoIp = GeoIp::Ptr(new GeoIp));50 UbuntuLocationService(const GeoIp::Ptr& geoIp = GeoIp::Ptr(new GeoIp));
4051 unity::scopes::Location location() const;
41 virtual ~UbuntuLocationService();52 bool hasLocation() const;
4253 bool isActive() const;
43 unity::scopes::Location location() const override;54 QSharedPointer<Token> activate();
4455
45 bool hasLocation() const override;56public Q_SLOTS:
4657 void requestInitialLocation();
47 bool isActive() const override;
48
49 QSharedPointer<Token> activate() override;
5058
51Q_SIGNALS:59Q_SIGNALS:
60 // emited when location changes and only when access has been granted by apparmor
61 void locationChanged();
62
63 void locationTimeout();
64
65 // emited when geoip lookup finishes (including initial lookup on startup). regardless of apparmor permissions
66 // (receiving it doesn't mean position updates are allowed).
67 void geoIpLookupFinished();
68 void activeChanged();
69 void accessDenied();
52 void enqueueActivate();70 void enqueueActivate();
53
54 void enqueueDeactivate();71 void enqueueDeactivate();
5572
73protected Q_SLOTS:
74 void doActivate();
75 void doDeactivate();
76 void update();
77 void positionChanged(const QGeoPositionInfo& update);
78 void onPositionUpdateTimeout();
79 void onError(QGeoPositionInfoSource::Error positioningError);
80 void requestFinished(const GeoIp::Result& result);
81
56protected:82protected:
57 class Priv;83 bool m_active;
5884 QGeoPositionInfoSource *m_locationSource;
59 QThread m_thread;85 QGeoPositionInfo m_lastLocation;
6086 bool m_locationUpdatedAtLeastOnce = false;
61 QSharedPointer<Priv> p;87 int m_activationCount = 0;
88 QTimer m_geoipTimer;
89 QTimer m_deactivateTimer;
90 GeoIp::Ptr m_geoIp;
91 GeoIp::Result m_result;
62};92};
6393
64} // namespace scopes_ng94} // namespace scopes_ng
6595
=== modified file 'tests/CMakeLists.txt'
--- tests/CMakeLists.txt 2016-04-01 10:38:21 +0000
+++ tests/CMakeLists.txt 2016-06-10 15:06:19 +0000
@@ -7,7 +7,6 @@
7add_subdirectory(data)7add_subdirectory(data)
88
9add_definitions(9add_definitions(
10 -DGEOIP_SERVER_BINARY="${CMAKE_CURRENT_SOURCE_DIR}/geoip.ubuntu.com.py"
11 -DTEST_DATA_DIR="${TEST_DATA_DIR}"10 -DTEST_DATA_DIR="${TEST_DATA_DIR}"
12 -DTEST_RUNTIME_CONFIG="${TEST_RUNTIME_CONFIG}"11 -DTEST_RUNTIME_CONFIG="${TEST_RUNTIME_CONFIG}"
13 -DTEST_SETTINGS_UNICODE="${CMAKE_CURRENT_SOURCE_DIR}/data/settings-unicode.ini"12 -DTEST_SETTINGS_UNICODE="${CMAKE_CURRENT_SOURCE_DIR}/data/settings-unicode.ini"
@@ -18,7 +17,6 @@
18 ${CMAKE_SOURCE_DIR}/src17 ${CMAKE_SOURCE_DIR}/src
19 ${CMAKE_CURRENT_BINARY_DIR}18 ${CMAKE_CURRENT_BINARY_DIR}
20 ${SCOPESLIB_INCLUDE_DIRS}19 ${SCOPESLIB_INCLUDE_DIRS}
21 ${UBUNTU_LOCATION_SERVICE_INCLUDE_DIRS}
22 ${QTDBUSTEST_INCLUDE_DIRS}20 ${QTDBUSTEST_INCLUDE_DIRS}
23 ${QTDBUSMOCK_INCLUDE_DIRS}21 ${QTDBUSMOCK_INCLUDE_DIRS}
24 ${GSETTINGSQT_INCLUDE_DIRS}22 ${GSETTINGSQT_INCLUDE_DIRS}
@@ -65,7 +63,6 @@
65 filtersendtoendtest63 filtersendtoendtest
66 optionselectorfiltertest64 optionselectorfiltertest
67 favoritestest65 favoritestest
68 locationtest
69 overviewtest66 overviewtest
70 previewtest67 previewtest
71 resultstest68 resultstest
7269
=== removed file 'tests/geoip.ubuntu.com.py'
--- tests/geoip.ubuntu.com.py 2014-07-23 11:10:53 +0000
+++ tests/geoip.ubuntu.com.py 1970-01-01 00:00:00 +0000
@@ -1,66 +0,0 @@
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#
4# Copyright (C) 2014 Canonical Ltd
5#
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU Lesser General Public License version 3 as
8# published by the Free Software Foundation.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU Lesser General Public License for more details.
14#
15# You should have received a copy of the GNU Lesser General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17#
18# Authored by: Pete Woods <pete.woods@canonical.com>
19
20import tornado.httpserver
21import tornado.ioloop
22import tornado.netutil
23import tornado.web
24import sys
25
26RESPONSE = '''<Response>
27<Ip>1.2.3.4</Ip>
28<Status>OK</Status>
29<CountryCode>GB</CountryCode>
30<CountryCode3>GBR</CountryCode3>
31<CountryName>United Kingdom</CountryName>
32<RegionCode>H2</RegionCode>
33<RegionName>Lancashire</RegionName>
34<City>Accrington</City>
35<ZipPostalCode>BB5</ZipPostalCode>
36<Latitude>55.7654</Latitude>
37<Longitude>-2.7467</Longitude>
38<AreaCode>0</AreaCode>
39<TimeZone>Europe/London</TimeZone>
40</Response>
41'''
42
43class Lookup(tornado.web.RequestHandler):
44 def get(self):
45 sys.stderr.write('GeoIP location requested\n')
46 sys.stderr.flush()
47
48 self.write(RESPONSE)
49 self.finish()
50
51def new_app():
52 application = tornado.web.Application([
53 (r"/lookup", Lookup),
54 ], gzip=True)
55 sockets = tornado.netutil.bind_sockets(0, '127.0.0.1')
56 server = tornado.httpserver.HTTPServer(application)
57 server.add_sockets(sockets)
58
59 sys.stdout.write('%d\n' % sockets[0].getsockname()[1])
60 sys.stdout.flush()
61
62 return application
63
64if __name__ == "__main__":
65 application = new_app()
66 tornado.ioloop.IOLoop.instance().start()
670
=== removed file 'tests/locationtest.cpp'
--- tests/locationtest.cpp 2015-02-26 18:02:32 +0000
+++ tests/locationtest.cpp 1970-01-01 00:00:00 +0000
@@ -1,234 +0,0 @@
1/*
2 * Copyright (C) 2013-2014 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Pete Woods <pete.woods@canonical.com>
18 */
19
20#include "ubuntulocationservice.h"
21
22#include <QProcess>
23#include <QSignalSpy>
24#include <QTest>
25#include <string>
26
27#include <libqtdbusmock/DBusMock.h>
28#include <libqtdbustest/DBusTestRunner.h>
29
30using namespace std;
31using namespace scopes_ng;
32using namespace unity::scopes;
33using namespace QtDBusTest;
34using namespace QtDBusMock;
35
36namespace
37{
38
39static const char* LOCATION_SERVICE_NAME = "com.ubuntu.location.Service";
40static const char* LOCATION_SERVICE_PATH = "/com/ubuntu/location/Service";
41static const char* LOCATION_SERVICE_INTERFACE = "com.ubuntu.location.Service";
42
43static const char* SESSION_NAME = "com.ubuntu.location.Service.Session";
44static const QString SESSION_PATH = "/com/ubuntu/location/session/%1";
45static const QString SESSION_INTERFACE = "com.ubuntu.location.Service.Session";
46
47static Variant geoip()
48{
49 VariantMap result;
50 result["area_code"] = "0";
51 result["city"] = "Accrington";
52 result["country_code"] = "GB";
53 result["country_name"] = "United Kingdom";
54 result["horizontal_accuracy"] = 100000.0;
55 result["latitude"] = 55.76540;
56 result["longitude"] = -2.74670;
57 result["region_code"] = "H2";
58 result["region_name"] = "Lancashire";
59 result["zip_postal_code"] = "BB5";
60 return Variant(result);
61}
62
63static Variant gps()
64{
65 VariantMap result;
66 result["altitude"] = 3.0;
67 result["area_code"] = "0";
68 result["city"] = "Accrington";
69 result["country_code"] = "GB";
70 result["country_name"] = "United Kingdom";
71 result["horizontal_accuracy"] = 4.0;
72 result["latitude"] = 1.0;
73 result["longitude"] = 2.0;
74 result["region_code"] = "H2";
75 result["region_name"] = "Lancashire";
76 result["vertical_accuracy"] = 5.0;
77 result["zip_postal_code"] = "BB5";
78 return Variant(result);
79}
80
81class LocationTest: public QObject
82{
83Q_OBJECT
84
85public:
86 LocationTest() :
87 mock(dbus)
88 {
89 }
90
91private:
92 OrgFreedesktopDBusMockInterface& interface()
93 {
94 return mock.mockInterface(LOCATION_SERVICE_NAME, LOCATION_SERVICE_PATH,
95 LOCATION_SERVICE_INTERFACE,
96 QDBusConnection::SystemBus);
97 }
98
99 OrgFreedesktopDBusMockInterface& session(int id)
100 {
101 return mock.mockInterface(SESSION_NAME, SESSION_PATH.arg(id),
102 SESSION_INTERFACE, QDBusConnection::SystemBus);
103 }
104
105 DBusTestRunner dbus;
106
107 DBusMock mock;
108
109 QScopedPointer<LocationService> locationService;
110
111 QProcess geoIpServer;
112
113 QUrl url;
114
115private Q_SLOTS:
116 void initTestCase()
117 {
118 DBusMock::registerMetaTypes();
119
120 // Register the main interface object
121 mock.registerCustomMock(LOCATION_SERVICE_NAME, LOCATION_SERVICE_PATH,
122 LOCATION_SERVICE_INTERFACE,
123 QDBusConnection::SystemBus);
124
125 // Register the first session object
126 mock.registerCustomMock(SESSION_NAME, SESSION_PATH.arg(0),
127 SESSION_INTERFACE,
128 QDBusConnection::SystemBus);
129
130 dbus.startServices();
131
132 // Set up the main interface
133 {
134 interface().AddMethod(LOCATION_SERVICE_INTERFACE,
135 "CreateSessionForCriteria", "bbbbdbbb", "o",
136 "ret='/com/ubuntu/location/session/0'");
137 }
138
139 // Set up the first session
140 {
141 session(0).AddMethod(SESSION_INTERFACE, "StartPositionUpdates", "", "", "");
142 session(0).AddMethod(SESSION_INTERFACE, "StopPositionUpdates", "", "", "");
143 }
144
145 geoIpServer.setProcessChannelMode(QProcess::ForwardedErrorChannel);
146 geoIpServer.start(GEOIP_SERVER_BINARY);
147 QVERIFY(geoIpServer.waitForStarted());
148 QVERIFY(geoIpServer.waitForReadyRead());
149
150 url = "http://127.0.0.1:" + geoIpServer.readAllStandardOutput().trimmed() + "/lookup";
151 }
152
153 void cleanupTestCase() {
154 geoIpServer.terminate();
155 QVERIFY(geoIpServer.waitForFinished());
156 }
157
158 void init()
159 {
160 locationService.reset(new UbuntuLocationService(GeoIp::Ptr(new GeoIp(url))));
161 }
162
163 void cleanup()
164 {
165 locationService.reset();
166 }
167
168 void compareVariant(const Variant& expected_in, const Variant& actual_in)
169 {
170 VariantMap expected(expected_in.get_dict());
171 VariantMap actual(actual_in.get_dict());
172
173 QVERIFY2(
174 expected.size() <= actual.size(),
175 qPrintable(QString("We need at least %1 entries, had %2").arg(
176 expected.size()).arg(actual.size())));
177 for(const auto entry: expected)
178 {
179 QVERIFY(actual.find(entry.first) != actual.end());
180
181 Variant expectedVariant = entry.second;
182 Variant actualVariant = actual[entry.first];
183
184 if (expectedVariant.which() == Variant::Double)
185 {
186 bool comparison = qFuzzyCompare(expectedVariant.get_double(), actualVariant.get_double());
187 if (!comparison)
188 {
189 qWarning() << "Comparison:" << expectedVariant.get_double() << "!=" << actualVariant.get_double();
190 }
191 QVERIFY(comparison);
192 }
193 else
194 {
195 QCOMPARE(expectedVariant, actualVariant);
196 }
197 }
198 }
199
200 void testLocation()
201 {
202 QSignalSpy spy(locationService.data(), SIGNAL(locationChanged()));
203 auto token = locationService->activate();
204
205 // The GeoIP HTTP call should return now
206 QVERIFY(spy.wait());
207 compareVariant(geoip(), Variant(locationService->location().serialize()));
208
209 // Call the object that the location service client creates
210 QDBusInterface remoteObject(":1.4", SESSION_PATH.arg(0), SESSION_INTERFACE,
211 QDBusConnection::systemBus());
212
213 QString errorMessage("never called");
214 for (int i = 0; i < 10 && !errorMessage.isEmpty(); ++i)
215 {
216 QDBusMessage reply = remoteObject.callWithArgumentList(
217 QDBus::Block,
218 "UpdatePosition",
219 QVariantList() << 1.0 << 2.0 << true << 3.0 << true << 4.0
220 << true << 5.0 << qint64(1234));
221 errorMessage = reply.errorMessage();
222 }
223 QCOMPARE(QString(), errorMessage);
224
225 // The GPS update should return now
226 QVERIFY(spy.wait());
227 QTRY_COMPARE((unsigned int) locationService->location().serialize().size(), 12u);
228 compareVariant(gps(), Variant(locationService->location().serialize()));
229 }
230};
231
232}
233QTEST_GUILESS_MAIN(LocationTest)
234#include <locationtest.moc>

Subscribers

People subscribed via source and target branches

to all changes: