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
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2016-05-05 09:40:46 +0000
3+++ CMakeLists.txt 2016-06-10 15:06:19 +0000
4@@ -47,6 +47,7 @@
5 find_package(Qt5Qml)
6 find_package(Qt5Quick)
7 find_package(Qt5Gui)
8+find_package(Qt5Positioning)
9 find_package(Qt5Test)
10 find_package(Boost COMPONENTS regex REQUIRED)
11
12@@ -54,7 +55,6 @@
13 pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=12)
14
15 pkg_check_modules(GSETTINGSQT REQUIRED gsettings-qt)
16-pkg_check_modules(UBUNTU_LOCATION_SERVICE REQUIRED ubuntu-location-service)
17 pkg_check_modules(ONLINE_ACCOUNTS_CLIENT REQUIRED OnlineAccountsClient)
18
19 # Standard install paths
20@@ -122,6 +122,6 @@
21 add_subdirectory(src)
22 add_subdirectory(tests)
23 add_subdirectory(data)
24-add_subdirectory(tools)
25+#add_subdirectory(tools)
26 add_subdirectory(po)
27 add_subdirectory(docs)
28
29=== modified file 'debian/control'
30--- debian/control 2016-05-25 08:16:27 +0000
31+++ debian/control 2016-06-10 15:06:19 +0000
32@@ -13,17 +13,13 @@
33 libgsettings-qt-dev (>= 0.1),
34 libqtdbustest1-dev (>= 0.2),
35 libqtdbusmock1-dev (>= 0.2),
36- libubuntu-location-service-dev (>= 2.0.1),
37 pkg-config,
38 python3-dev,
39-# the two python- lines should be removed once lp bug #1582280 is fixed
40- python-singledispatch | python-tornado (<< 4.3.0),
41- python-backports-abc | python-tornado (<< 4.3.0),
42- python-tornado,
43 python3-sphinx,
44 qtdeclarative5-dev,
45 qtdeclarative5-dev-tools,
46 qtdeclarative5-qtquick2-plugin,
47+ qtpositioning5-dev,
48 unity-schemas (>= 7.3.1),
49 libonline-accounts-client-dev,
50 intltool,
51@@ -61,7 +57,7 @@
52 Description: QML plugin for Scopes
53 Plugin to integrate scopes with the Unity shell
54
55-Package: libscope-harness3
56+Package: libscope-harness2
57 Section: libdevel
58 Architecture: any
59 Multi-Arch: same
60@@ -77,7 +73,7 @@
61 Multi-Arch: same
62 Depends: ${misc:Depends},
63 ${shlibs:Depends},
64- libscope-harness3 (= ${binary:Version})
65+ libscope-harness2 (= ${binary:Version})
66 Description: Test harness for Unity scopes
67 Drive Unity scopes with a simple synchronous API. Make assertions
68 about results. Development files.
69
70=== modified file 'debian/control.in'
71--- debian/control.in 2016-05-25 08:16:18 +0000
72+++ debian/control.in 2016-06-10 15:06:19 +0000
73@@ -13,17 +13,13 @@
74 libgsettings-qt-dev (>= 0.1),
75 libqtdbustest1-dev (>= 0.2),
76 libqtdbusmock1-dev (>= 0.2),
77- libubuntu-location-service-dev (>= 2.0.1),
78 pkg-config,
79 python3-dev,
80-# the two python- lines should be removed once lp bug #1582280 is fixed
81- python-singledispatch | python-tornado (<< 4.3.0),
82- python-backports-abc | python-tornado (<< 4.3.0),
83- python-tornado,
84 python3-sphinx,
85 qtdeclarative5-dev,
86 qtdeclarative5-dev-tools,
87 qtdeclarative5-qtquick2-plugin,
88+ qtpositioning5-dev,
89 unity-schemas (>= 7.3.1),
90 libonline-accounts-client-dev,
91 intltool,
92
93=== modified file 'po/POTFILES.in'
94--- po/POTFILES.in 2016-03-30 09:43:27 +0000
95+++ po/POTFILES.in 2016-06-10 15:06:19 +0000
96@@ -6,11 +6,10 @@
97 tests/previewtest.cpp
98 tests/overviewtest.cpp
99 tests/departmentstest.cpp
100-tests/locationtest.cpp
101 tests/filtersendtoendtest.cpp
102+tests/scopesinittest.cpp
103 tests/favoritestest.cpp
104 tests/optionselectorfiltertest.cpp
105-tests/data/scopes/scopes.cpp
106 tests/data/mock-scope-departments/mock-scope-departments.cpp
107 tests/data/mock-scope-filters/mock-scope-filters.cpp
108 tests/data/mock-scope-ttl/mock-scope-ttl.cpp
109@@ -64,6 +63,7 @@
110 src/python/scope_harness/results-view-py.cpp
111 src/python/scope_harness/preview-widget-py.cpp
112 src/python/scope_harness/preview-widget-list-py.cpp
113+src/Unity/rangeinputfilter.cpp
114 src/Unity/previewwidgetmodel.cpp
115 src/Unity/departmentnode.cpp
116 src/Unity/logintoaccount.cpp
117@@ -75,16 +75,19 @@
118 src/Unity/geoip.cpp
119 src/Unity/plugin.cpp
120 src/Unity/scopes.cpp
121+src/Unity/filtergroupwidget.cpp
122 src/Unity/filters.cpp
123 src/Unity/optionselectoroptions.cpp
124+src/Unity/locationaccesshelper.cpp
125 src/Unity/settingsmodel.cpp
126 src/Unity/utils.cpp
127 src/Unity/optionselectorfilter.cpp
128 src/Unity/resultsmap.cpp
129-src/Unity/locationservice.cpp
130 src/Unity/ubuntulocationservice.cpp
131+src/Unity/valueslidervalues.cpp
132 src/Unity/iconutils.cpp
133 src/Unity/department.cpp
134+src/Unity/valuesliderfilter.cpp
135 src/Unity/categories.cpp
136 src/Unity/overviewresults.cpp
137 src/Unity/resultsmodel.cpp
138@@ -134,16 +137,20 @@
139 src/Unity/overviewresults.h
140 src/Unity/plugin.h
141 src/Unity/department.h
142+src/Unity/valueslidervalues.h
143+src/Unity/locationaccesshelper.h
144 src/Unity/scope.h
145+src/Unity/rangeinputfilter.h
146 src/Unity/filters.h
147 src/Unity/logintoaccount.h
148 src/Unity/overviewcategories.h
149 src/Unity/modelupdate.h
150-src/Unity/locationservice.h
151 src/Unity/overviewscope.h
152 src/Unity/settingsmodel.h
153+src/Unity/valuesliderfilter.h
154 src/Unity/resultsmodel.h
155 src/Unity/geoip.h
156+src/Unity/filtergroupwidget.h
157 src/Unity/resultsmap.h
158 src/Unity/collectors.h
159 src/Unity/previewwidgetmodel.h
160
161=== modified file 'src/CMakeLists.txt'
162--- src/CMakeLists.txt 2015-06-17 06:56:58 +0000
163+++ src/CMakeLists.txt 2016-06-10 15:06:19 +0000
164@@ -1,14 +1,8 @@
165-
166-include_directories(
167- ${UBUNTU_LOCATION_SERVICE_INCLUDE_DIRS}
168-)
169-
170 set(
171 SCOPES_SHELL_DEPENDENCIES
172 ${SCOPESLIB_LDFLAGS}
173 ${GSETTINGSQT_LDFLAGS}
174 ${U1DB_LDFLAGS}
175- ${UBUNTU_LOCATION_SERVICE_LDFLAGS}
176 ${ONLINE_ACCOUNTS_CLIENT_LDFLAGS}
177 )
178
179@@ -19,9 +13,9 @@
180 Gui
181 Network
182 Qml
183+ Positioning
184 )
185
186 add_subdirectory(Unity)
187 add_subdirectory(scope-harness)
188 add_subdirectory(python)
189-
190
191=== modified file 'src/Unity/CMakeLists.txt'
192--- src/Unity/CMakeLists.txt 2016-03-10 15:54:33 +0000
193+++ src/Unity/CMakeLists.txt 2016-06-10 15:06:19 +0000
194@@ -29,7 +29,7 @@
195 valueslidervalues.cpp
196 geoip.cpp
197 localization.h
198- locationservice.cpp
199+ locationaccesshelper.cpp
200 overviewcategories.cpp
201 overviewresults.cpp
202 overviewscope.cpp
203
204=== added file 'src/Unity/locationaccesshelper.cpp'
205--- src/Unity/locationaccesshelper.cpp 1970-01-01 00:00:00 +0000
206+++ src/Unity/locationaccesshelper.cpp 2016-06-10 15:06:19 +0000
207@@ -0,0 +1,105 @@
208+/*
209+ * Copyright (C) 2016 Canonical, Ltd.
210+ *
211+ * Authors:
212+ * Pawel Stolowski <pawel.stolowski@canonical.com>
213+ *
214+ * This program is free software; you can redistribute it and/or modify
215+ * it under the terms of the GNU General Public License as published by
216+ * the Free Software Foundation; version 3.
217+ *
218+ * This program is distributed in the hope that it will be useful,
219+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
220+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
221+ * GNU General Public License for more details.
222+ *
223+ * You should have received a copy of the GNU General Public License
224+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
225+ */
226+
227+#include "locationaccesshelper.h"
228+#include <QDir>
229+#include <QFile>
230+#include <QStandardPaths>
231+#include <QDebug>
232+
233+namespace scopes_ng
234+{
235+
236+const QString LocationAccessHelper::scopesLocationDotFile = ".scopesLocationPrompt";
237+
238+LocationAccessHelper::LocationAccessHelper(QObject *parent) :
239+ QObject(parent),
240+ m_dotFileExists(false),
241+ m_denied(true)
242+{
243+}
244+
245+void LocationAccessHelper::init()
246+{
247+ auto const path = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
248+ QDir locationPromptFile(path);
249+ m_dotFileExists = locationPromptFile.exists(scopesLocationDotFile);
250+
251+ if (m_dotFileExists) {
252+ // dot file exists, it means user was already prompted for location so we can
253+ // safely request location on startup without risking immediate trusted prompt.
254+ Q_EMIT requestInitialLocation();
255+ }
256+}
257+
258+bool LocationAccessHelper::trustedPromptWasShown() const
259+{
260+ return m_dotFileExists;
261+}
262+
263+bool LocationAccessHelper::isLocationAccessDenied() const
264+{
265+ return m_denied;
266+}
267+
268+void LocationAccessHelper::geoIpLookupFinished()
269+{
270+ qDebug() << "LocationAccessHelper::geoIpLookupFinished";
271+ // This signal is not interesting at the moment. If, however we need to refresh scopes on location update,
272+ // then it should be re-emited (forwarded) here and in positonChanged() below.
273+}
274+
275+void LocationAccessHelper::positionChanged()
276+{
277+ if (m_denied) {
278+ m_denied = false;
279+ Q_EMIT accessChanged();
280+ }
281+
282+ if (!m_dotFileExists) {
283+ createLocationPromptFile();
284+ }
285+}
286+
287+void LocationAccessHelper::accessDenied()
288+{
289+ qDebug() << "LocationAccessHelper::accessDenied";
290+ if (!m_denied) {
291+ m_denied = true;
292+ Q_EMIT accessChanged();
293+ }
294+
295+ if (!m_dotFileExists) {
296+ createLocationPromptFile();
297+ }
298+}
299+
300+void LocationAccessHelper::createLocationPromptFile()
301+{
302+ auto const path = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + "/" + scopesLocationDotFile;
303+ QFile locationPromptFile(path);
304+ if (locationPromptFile.open(QIODevice::WriteOnly)) {
305+ qDebug() << "Creating" << locationPromptFile.fileName();
306+ m_dotFileExists = true;
307+ } else {
308+ qWarning() << "Failed to create" << locationPromptFile.fileName();
309+ }
310+}
311+
312+}
313
314=== added file 'src/Unity/locationaccesshelper.h'
315--- src/Unity/locationaccesshelper.h 1970-01-01 00:00:00 +0000
316+++ src/Unity/locationaccesshelper.h 2016-06-10 15:06:19 +0000
317@@ -0,0 +1,56 @@
318+/*
319+ * Copyright (C) 2016 Canonical, Ltd.
320+ *
321+ * Authors:
322+ * Pawel Stolowski <pawel.stolowski@canonical.com>
323+ *
324+ * This program is free software; you can redistribute it and/or modify
325+ * it under the terms of the GNU General Public License as published by
326+ * the Free Software Foundation; version 3.
327+ *
328+ * This program is distributed in the hope that it will be useful,
329+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
330+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
331+ * GNU General Public License for more details.
332+ *
333+ * You should have received a copy of the GNU General Public License
334+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
335+ */
336+
337+#pragma once
338+
339+#include <QObject>
340+
341+namespace scopes_ng
342+{
343+
344+class Q_DECL_EXPORT LocationAccessHelper: public QObject
345+{
346+ Q_OBJECT
347+
348+public:
349+ LocationAccessHelper(QObject *parent = nullptr);
350+ void init();
351+
352+ bool trustedPromptWasShown() const;
353+ bool isLocationAccessDenied() const;
354+
355+public Q_SLOTS:
356+ void accessDenied();
357+ void positionChanged();
358+ void geoIpLookupFinished();
359+
360+Q_SIGNALS:
361+ void accessChanged();
362+ void requestInitialLocation();
363+
364+private:
365+ void createLocationPromptFile();
366+
367+ bool m_dotFileExists;
368+ bool m_denied;
369+
370+ static const QString scopesLocationDotFile;
371+};
372+
373+}
374
375=== removed file 'src/Unity/locationservice.cpp'
376--- src/Unity/locationservice.cpp 2014-07-15 08:37:40 +0000
377+++ src/Unity/locationservice.cpp 1970-01-01 00:00:00 +0000
378@@ -1,26 +0,0 @@
379-/*
380- * Copyright (C) 2014 Canonical, Ltd.
381- *
382- * This program is free software; you can redistribute it and/or modify
383- * it under the terms of the GNU General Public License as published by
384- * the Free Software Foundation; version 3.
385- *
386- * This program is distributed in the hope that it will be useful,
387- * but WITHOUT ANY WARRANTY; without even the implied warranty of
388- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
389- * GNU General Public License for more details.
390- *
391- * You should have received a copy of the GNU General Public License
392- * along with this program. If not, see <http://www.gnu.org/licenses/>.
393- *
394- * Authors:
395- * Pete Woods <pete.woods@canonical.com>
396- */
397-
398-#include <locationservice.h>
399-
400-using namespace scopes_ng;
401-
402-LocationService::LocationService() {
403-
404-}
405
406=== removed file 'src/Unity/locationservice.h'
407--- src/Unity/locationservice.h 2015-02-26 18:02:32 +0000
408+++ src/Unity/locationservice.h 1970-01-01 00:00:00 +0000
409@@ -1,72 +0,0 @@
410-/*
411- * Copyright (C) 2014 Canonical, Ltd.
412- *
413- * This program is free software; you can redistribute it and/or modify
414- * it under the terms of the GNU General Public License as published by
415- * the Free Software Foundation; version 3.
416- *
417- * This program is distributed in the hope that it will be useful,
418- * but WITHOUT ANY WARRANTY; without even the implied warranty of
419- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
420- * GNU General Public License for more details.
421- *
422- * You should have received a copy of the GNU General Public License
423- * along with this program. If not, see <http://www.gnu.org/licenses/>.
424- *
425- * Authors:
426- * Pete Woods <pete.woods@canonical.com>
427- */
428-
429-#ifndef LOCATIONSERVICE_H
430-#define LOCATIONSERVICE_H
431-
432-#include <QObject>
433-
434-#include <com/ubuntu/location/heading.h>
435-#include <com/ubuntu/location/position.h>
436-#include <com/ubuntu/location/update.h>
437-#include <com/ubuntu/location/velocity.h>
438-
439-#include <unity/scopes/Location.h>
440-
441-namespace scopes_ng
442-{
443-
444-class Q_DECL_EXPORT LocationService : public QObject
445-{
446- Q_OBJECT
447-
448- Q_PROPERTY(unity::scopes::Location location READ location NOTIFY locationChanged)
449-
450- Q_PROPERTY(bool active READ isActive NOTIFY activeChanged)
451-
452-public:
453- typedef QSharedPointer<LocationService> Ptr;
454-
455- class Token : public QObject
456- {
457- };
458-
459- LocationService();
460-
461- virtual ~LocationService() = default;
462-
463- virtual unity::scopes::Location location() const = 0;
464-
465- virtual bool hasLocation() const = 0;
466-
467- virtual bool isActive() const = 0;
468-
469-public Q_SLOTS:
470- virtual QSharedPointer<Token> activate() = 0;
471-
472-Q_SIGNALS:
473- void locationChanged();
474-
475- void activeChanged();
476-
477-};
478-
479-} // namespace scopes_ng
480-
481-#endif /* LOCATIONSERVICE_H_ */
482
483=== modified file 'src/Unity/overviewscope.cpp'
484--- src/Unity/overviewscope.cpp 2016-02-04 15:19:37 +0000
485+++ src/Unity/overviewscope.cpp 2016-06-10 15:06:19 +0000
486@@ -145,7 +145,7 @@
487 categories->updateOtherScopes(otherScopes, scopeIdToName);
488 }
489
490-void OverviewScope::dispatchSearch()
491+void OverviewScope::dispatchSearch(bool)
492 {
493 qWarning() << "Search is not implemented for Manage Dash";
494 }
495
496=== modified file 'src/Unity/overviewscope.h'
497--- src/Unity/overviewscope.h 2016-02-04 15:19:37 +0000
498+++ src/Unity/overviewscope.h 2016-06-10 15:06:19 +0000
499@@ -37,7 +37,7 @@
500 /* getters */
501 QString id() const override;
502
503- void dispatchSearch() override;
504+ void dispatchSearch(bool) override;
505 void setActive(const bool) override;
506
507 void updateFavorites(const QStringList& favorites);
508
509=== modified file 'src/Unity/scope.cpp'
510--- src/Unity/scope.cpp 2016-05-25 08:16:09 +0000
511+++ src/Unity/scope.cpp 2016-06-10 15:06:19 +0000
512@@ -24,8 +24,8 @@
513 // local
514 #include "categories.h"
515 #include "collectors.h"
516+#include "locationaccesshelper.h"
517 #include "previewmodel.h"
518-#include "locationservice.h"
519 #include "utils.h"
520 #include "scopes.h"
521 #include "settingsmodel.h"
522@@ -77,7 +77,8 @@
523
524 Scope::Ptr Scope::newInstance(scopes_ng::Scopes* parent)
525 {
526- return Scope::Ptr(new Scope(parent), &QObject::deleteLater);
527+ auto scope = Scope::Ptr(new Scope(parent), &QObject::deleteLater);
528+ return scope;
529 }
530
531 Scope::Scope(scopes_ng::Scopes* parent) :
532@@ -696,7 +697,7 @@
533 m_filterState = filterState;
534 }
535
536-void Scope::dispatchSearch()
537+void Scope::dispatchSearch(bool programmaticSearch)
538 {
539 m_initialQueryDone = true;
540
541@@ -736,6 +737,20 @@
542 // (i.e. while the loading bar is visible).
543 update_child_scopes();
544
545+ // handle the case where single scope is refreshed multiple times without switching
546+ // to another scope, which would never trigger location prompt.
547+ if (m_scopeMetadata && m_scopeMetadata->location_data_needed() && !m_locationToken)
548+ {
549+ if (m_isActive)
550+ {
551+ Q_ASSERT(m_scopesInstance);
552+
553+ if ((!programmaticSearch) || m_scopesInstance->locationAccessHelper()->trustedPromptWasShown()) {
554+ m_locationToken = m_locationService->activate();
555+ }
556+ }
557+ }
558+
559 if (m_proxy) {
560 scopes::SearchMetadata meta(m_cardinality, QLocale::system().name().toStdString(), m_formFactor.toStdString());
561 auto const userAgent = m_scopesInstance->userAgentString();
562@@ -764,6 +779,7 @@
563
564 scopes::SearchListenerBase::SPtr listener(new SearchResultReceiver(this));
565 m_searchController->setListener(listener);
566+
567 try {
568 qDebug() << "Dispatching search:" << id() << m_searchQuery << m_currentNavigationId;
569 scopes::QueryCtrlProxy controller = m_queryUserData ?
570@@ -792,6 +808,11 @@
571 m_customizations = converted.toMap();
572 Q_EMIT customizationsChanged();
573
574+ createSettingsModel();
575+}
576+
577+void Scope::createSettingsModel()
578+{
579 try
580 {
581 scopes::Variant settings_definitions;
582@@ -806,10 +827,20 @@
583 shareDir = QDir::home().filePath(QStringLiteral(".config/unity-scopes"));
584 }
585
586+ Q_ASSERT(m_scopesInstance);
587+
588 m_settingsModel.reset(
589 new SettingsModel(shareDir, id(),
590- scopeVariantToQVariant(settings_definitions), this));
591+ scopeVariantToQVariant(settings_definitions),
592+ !m_scopesInstance->locationAccessHelper()->isLocationAccessDenied(),
593+ this));
594+
595 QObject::connect(m_settingsModel.data(), &SettingsModel::settingsChanged, this, &Scope::invalidateResults);
596+
597+ // If the scope needs location, then changes to global location access need to be monitored.
598+ if (m_scopeMetadata->location_data_needed()) {
599+ QObject::connect(m_scopesInstance->locationAccessHelper().data(), &LocationAccessHelper::accessChanged, this, &Scope::locationAccessChanged);
600+ }
601 }
602 catch (unity::scopes::NotFoundException&)
603 {
604@@ -906,6 +937,16 @@
605 return m_settingsModel.data();
606 }
607
608+void Scope::locationAccessChanged()
609+{
610+ qDebug() << "Location access changed, recreating settings model for scope" << id();
611+ createSettingsModel();
612+
613+ // Force child scopes refresh
614+ m_childScopesDirty = true;
615+ update_child_scopes();
616+}
617+
618 bool Scope::require_child_scopes_refresh() const
619 {
620 if (m_settingsModel && m_scopesInstance)
621@@ -1116,11 +1157,7 @@
622
623 if (m_scopeMetadata && m_scopeMetadata->location_data_needed())
624 {
625- if (m_isActive)
626- {
627- m_locationToken = m_locationService->activate();
628- }
629- else
630+ if (!m_isActive)
631 {
632 m_locationToken.reset();
633 }
634
635=== modified file 'src/Unity/scope.h'
636--- src/Unity/scope.h 2016-05-06 07:32:13 +0000
637+++ src/Unity/scope.h 2016-06-10 15:06:19 +0000
638@@ -43,7 +43,8 @@
639 #include "collectors.h"
640 #include "departmentnode.h"
641 #include "department.h"
642-#include "locationservice.h"
643+#include "ubuntulocationservice.h"
644+#include "locationaccesshelper.h"
645
646 namespace scopes_ng
647 {
648@@ -51,7 +52,6 @@
649 class Categories;
650 class PushEvent;
651 class PreviewModel;
652-class LocationService;
653 class SettingsModel;
654 class Scopes;
655
656@@ -185,7 +185,7 @@
657 public Q_SLOTS:
658 void invalidateChildScopes();
659 void invalidateResults();
660- virtual void dispatchSearch();
661+ virtual void dispatchSearch(bool programmaticSearch = false);
662 void setSearchInProgress(bool searchInProgress);
663 void setActivationInProgress(bool activationInProgress);
664
665@@ -200,6 +200,7 @@
666 void flushUpdates(bool finalize = false);
667 void metadataRefreshed();
668 void departmentModelDestroyed(QObject* obj);
669+ void locationAccessChanged();
670 void filterStateChanged();
671 void previewModelDestroyed(QObject *obj);
672
673@@ -208,6 +209,7 @@
674
675 void setStatus(unity::shell::scopes::ScopeInterface::Status status);
676 void invalidateLastSearch();
677+ void createSettingsModel();
678
679 unity::scopes::ScopeProxy proxy() const;
680
681@@ -278,8 +280,8 @@
682 QMultiMap<QString, Department*> m_departmentModels;
683 QMap<Department*, QString> m_inverseDepartments;
684 QMetaObject::Connection m_metadataConnection;
685- QSharedPointer<LocationService> m_locationService;
686- QSharedPointer<LocationService::Token> m_locationToken;
687+ QSharedPointer<UbuntuLocationService> m_locationService;
688+ QSharedPointer<UbuntuLocationService::Token> m_locationToken;
689 QNetworkConfigurationManager m_network_manager;
690 QList<PreviewModel*> m_previewModels;
691 };
692
693=== modified file 'src/Unity/scopes.cpp'
694--- src/Unity/scopes.cpp 2016-05-05 09:41:13 +0000
695+++ src/Unity/scopes.cpp 2016-06-10 15:06:19 +0000
696@@ -107,6 +107,7 @@
697 , m_listThread(nullptr)
698 , m_loaded(false)
699 , m_prepopulateFirstScope(true)
700+ , m_locationAccessHelper(new LocationAccessHelper(nullptr))
701 , m_priv(new Priv())
702 {
703 QByteArray noFav = qgetenv("UNITY_SCOPES_NO_FAVORITES");
704@@ -136,6 +137,11 @@
705 connect(&m_registryRefreshTimer, SIGNAL(timeout()), this, SLOT(scopeRegistryChanged()));
706
707 m_locationService.reset(new UbuntuLocationService());
708+ QObject::connect(m_locationAccessHelper.data(), &LocationAccessHelper::requestInitialLocation, m_locationService.data(), &UbuntuLocationService::requestInitialLocation);
709+ QObject::connect(m_locationService.data(), &UbuntuLocationService::accessDenied, m_locationAccessHelper.data(), &LocationAccessHelper::accessDenied);
710+ QObject::connect(m_locationService.data(), &UbuntuLocationService::locationChanged, m_locationAccessHelper.data(), &LocationAccessHelper::positionChanged);
711+ QObject::connect(m_locationService.data(), &UbuntuLocationService::geoIpLookupFinished, m_locationAccessHelper.data(), &LocationAccessHelper::geoIpLookupFinished);
712+ m_locationAccessHelper->init();
713
714 createUserAgentString();
715
716@@ -162,6 +168,11 @@
717 m_scopesToDelete.clear();
718 }
719
720+QSharedPointer<LocationAccessHelper> Scopes::locationAccessHelper() const
721+{
722+ return m_locationAccessHelper;
723+}
724+
725 int Scopes::rowCount(const QModelIndex& parent) const
726 {
727 Q_UNUSED(parent)
728@@ -330,9 +341,15 @@
729 }
730 else
731 {
732+ qDebug() << "Waiting for initial location update";
733+
734 // Otherwise we have to wait for location data
735 // Either the the location data needs to change, or the timeout happens
736- connect(m_locationService.data(), &LocationService::locationChanged,
737+ connect(m_locationService.data(), &UbuntuLocationService::locationChanged,
738+ this, &Scopes::completeDiscoveryFinished);
739+ connect(m_locationService.data(), &UbuntuLocationService::accessDenied,
740+ this, &Scopes::completeDiscoveryFinished);
741+ connect(m_locationService.data(), &UbuntuLocationService::locationTimeout,
742 this, &Scopes::completeDiscoveryFinished);
743 connect(&m_startupQueryTimeout, &QTimer::timeout, this,
744 &Scopes::completeDiscoveryFinished);
745@@ -344,11 +361,16 @@
746
747 void Scopes::completeDiscoveryFinished()
748 {
749+ qDebug() << "Scopes discovery completed";
750 // Kill off everything that could potentially trigger the startup queries
751 m_startupQueryTimeout.stop();
752 disconnect(&m_startupQueryTimeout, &QTimer::timeout, this,
753 &Scopes::completeDiscoveryFinished);
754- disconnect(m_locationService.data(), &LocationService::locationChanged,
755+ disconnect(m_locationService.data(), &UbuntuLocationService::locationChanged,
756+ this, &Scopes::completeDiscoveryFinished);
757+ disconnect(m_locationService.data(), &UbuntuLocationService::accessDenied,
758+ this, &Scopes::completeDiscoveryFinished);
759+ disconnect(m_locationService.data(), &UbuntuLocationService::locationTimeout,
760 this, &Scopes::completeDiscoveryFinished);
761
762 processFavoriteScopes();
763@@ -376,7 +398,7 @@
764 qDebug() << "Pre-populating first scope";
765 scope->setSearchQuery(QLatin1String(""));
766 // must dispatch search explicitly since setSearchQuery will not do that for inactive scope
767- scope->dispatchSearch();
768+ scope->dispatchSearch(true);
769 }
770 }
771 }
772@@ -393,7 +415,7 @@
773 qDebug() << "Pre-populating scope" << scope->id();
774 scope->setSearchQuery(QLatin1String(""));
775 // must dispatch search explicitly since setSearchQuery will not do that for inactive scope
776- scope->dispatchSearch();
777+ scope->dispatchSearch(true);
778 }
779 }
780 break;
781@@ -791,7 +813,7 @@
782 return m_loaded;
783 }
784
785-LocationService::Ptr Scopes::locationService() const
786+UbuntuLocationService::Ptr Scopes::locationService() const
787 {
788 return m_locationService;
789 }
790
791=== modified file 'src/Unity/scopes.h'
792--- src/Unity/scopes.h 2016-04-18 10:10:25 +0000
793+++ src/Unity/scopes.h 2016-06-10 15:06:19 +0000
794@@ -22,6 +22,7 @@
795
796 #include <unity/shell/scopes/ScopesInterface.h>
797 #include "scope.h"
798+#include "locationaccesshelper.h"
799
800 // Qt
801 #include <QList>
802@@ -43,7 +44,8 @@
803 namespace scopes_ng
804 {
805
806-class LocationService;
807+class UbuntuLocationService;
808+class LocationAccessHelper;
809 class Scope;
810 class OverviewScope;
811
812@@ -75,12 +77,13 @@
813 unity::shell::scopes::ScopeInterface* overviewScope() const override;
814 Scope::Ptr overviewScopeSPtr() const;
815
816- QSharedPointer<LocationService> locationService() const;
817+ QSharedPointer<UbuntuLocationService> locationService() const;
818 QString userAgentString() const;
819
820 Scope::Ptr findTempScope(QString const& id) const;
821 void addTempScope(Scope::Ptr const& scope);
822 Q_INVOKABLE void closeScope(unity::shell::scopes::ScopeInterface* scope) override;
823+ QSharedPointer<LocationAccessHelper> locationAccessHelper() const;
824
825 Q_SIGNALS:
826 void metadataRefreshed();
827@@ -124,10 +127,11 @@
828 bool m_loaded;
829 bool m_prepopulateFirstScope;
830
831- QSharedPointer<LocationService> m_locationService;
832+ QSharedPointer<UbuntuLocationService> m_locationService;
833 QTimer m_startupQueryTimeout;
834 QTimer m_scopesToDeleteTimer;
835 QTimer m_registryRefreshTimer;
836+ QSharedPointer<LocationAccessHelper> m_locationAccessHelper;
837
838 unity::scopes::Runtime::SPtr m_scopesRuntime;
839 QMap<QString, Scope::Ptr> m_tempScopes;
840
841=== modified file 'src/Unity/settingsmodel.cpp'
842--- src/Unity/settingsmodel.cpp 2016-05-19 07:18:18 +0000
843+++ src/Unity/settingsmodel.cpp 2016-06-10 15:06:19 +0000
844@@ -74,7 +74,8 @@
845 } // namespace
846
847 SettingsModel::SettingsModel(const QDir& configDir, const QString& scopeId,
848- const QVariant& settingsDefinitions, QObject* parent,
849+ const QVariant& settingsDefinitions, bool isLocationGloballyEnabled,
850+ QObject* parent,
851 int settingsTimeout)
852 : SettingsModelInterface(parent), m_scopeId(scopeId), m_settingsTimeout(settingsTimeout),
853 m_requireChildScopesRefresh(false)
854@@ -98,6 +99,12 @@
855 QVariantMap data = it.toMap();
856 QString id = data[QStringLiteral("id")].toString();
857 QString displayName = data[QStringLiteral("displayName")].toString();
858+
859+ if (id == "internal.location" && !isLocationGloballyEnabled) {
860+ qDebug() << "Location setting ignored, waiting for global location access to be enabled first";
861+ continue;
862+ }
863+
864 QVariantMap properties;
865 QVariant defaultValue;
866 if (data.contains(QStringLiteral("displayValues")))
867
868=== modified file 'src/Unity/settingsmodel.h'
869--- src/Unity/settingsmodel.h 2016-05-19 07:06:55 +0000
870+++ src/Unity/settingsmodel.h 2016-06-10 15:06:19 +0000
871@@ -65,7 +65,8 @@
872
873 public:
874 explicit SettingsModel(const QDir& configDir, const QString& scopeId,
875- const QVariant& settingsDefinitions, QObject* parent = 0,
876+ const QVariant& settingsDefinitions, bool isLocationGloballyEnabled = true,
877+ QObject* parent = 0,
878 int settingsTimeout = 300);
879
880 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const
881
882=== modified file 'src/Unity/ubuntulocationservice.cpp'
883--- src/Unity/ubuntulocationservice.cpp 2015-09-08 12:32:05 +0000
884+++ src/Unity/ubuntulocationservice.cpp 2016-06-10 15:06:19 +0000
885@@ -20,27 +20,10 @@
886 #include "ubuntulocationservice.h"
887
888 #include <QDebug>
889-#include <QMutexLocker>
890-#include <QRunnable>
891-#include <QThreadPool>
892-#include <QTimer>
893-
894-#include <com/ubuntu/location/service/stub.h>
895-
896-#include <core/dbus/resolver.h>
897-#include <core/dbus/asio/executor.h>
898-
899-#include <memory>
900
901 using namespace std;
902-using namespace std::placeholders;
903 using namespace scopes_ng;
904
905-namespace cul = com::ubuntu::location;
906-namespace culs = com::ubuntu::location::service;
907-namespace culss = com::ubuntu::location::service::session;
908-namespace culu = com::ubuntu::location::units;
909-namespace dbus = core::dbus;
910 namespace scopes = unity::scopes;
911
912 namespace
913@@ -55,28 +38,9 @@
914 * Re-do the GeoIP call every 60 seconds
915 */
916 static const int GEOIP_INTERVAL = 60000;
917-
918- class DBusThread : public QThread
919- {
920-
921- public:
922- DBusThread(const dbus::Bus::Ptr& bus) :
923- m_bus(bus)
924- {
925- }
926-
927- protected:
928- void run() override
929- {
930- m_bus->run();
931- }
932-
933- dbus::Bus::Ptr m_bus;
934- };
935-
936 }
937
938-class UbuntuLocationService::TokenImpl: public LocationService::Token
939+class UbuntuLocationService::TokenImpl: public UbuntuLocationService::Token
940 {
941 Q_OBJECT
942
943@@ -96,220 +60,121 @@
944 void destroyed();
945 };
946
947-class UbuntuLocationService::Priv : public QObject
948-{
949-Q_OBJECT
950-
951-public:
952- Priv() :
953- m_lastLocationMutex(QMutex::Recursive), m_resultMutex(
954- QMutex::Recursive)
955- {
956- }
957-
958- void init(const GeoIp::Ptr& geoIp)
959- {
960- m_geoIp = geoIp;
961- m_geoIp->whollyMoveThread(thread());
962-
963- m_deactivateTimer.moveToThread(thread());
964- m_deactivateTimer.setInterval(DEACTIVATE_INTERVAL);
965- m_deactivateTimer.setSingleShot(true);
966- m_deactivateTimer.setTimerType(Qt::VeryCoarseTimer);
967-
968- m_geoipTimer.moveToThread(thread());
969- m_geoipTimer.setInterval(GEOIP_INTERVAL);
970- m_geoipTimer.setTimerType(Qt::CoarseTimer);
971-
972- QMetaObject::invokeMethod(m_geoIp.data(), "start", Qt::QueuedConnection);
973-
974- try
975- {
976- m_bus = make_shared<dbus::Bus>(dbus::WellKnownBus::system);
977- m_bus->install_executor(dbus::asio::make_executor(m_bus));
978-
979- m_dbusThread.reset(new DBusThread(m_bus));
980- m_dbusThread->start();
981-
982- m_locationService = dbus::resolve_service_on_bus<culs::Interface,
983- culs::Stub>(m_bus);
984- }
985- catch (exception& e)
986- {
987- qWarning() << e.what();
988- }
989-
990- // Wire up the deactivate timer
991- connect(&m_deactivateTimer, &QTimer::timeout, this, &Priv::update, Qt::QueuedConnection);
992-
993- // Wire up the network request finished timer
994- connect(m_geoIp.data(), &GeoIp::finished, this, &Priv::requestFinished, Qt::QueuedConnection);
995-
996- // Wire up the GeoIP repeat timer
997- connect(&m_geoipTimer, &QTimer::timeout, m_geoIp.data(), &GeoIp::start, Qt::QueuedConnection);
998- }
999-
1000- ~Priv()
1001- {
1002- if (m_bus)
1003- {
1004- m_bus->stop();
1005- }
1006-
1007- if (m_dbusThread && m_dbusThread->isRunning())
1008- {
1009- m_dbusThread->wait();
1010- }
1011- }
1012-
1013-Q_SIGNALS:
1014- void locationChanged();
1015-
1016-public Q_SLOTS:
1017- void update()
1018- {
1019- if (!m_locationService)
1020- {
1021- qWarning() << "Location service not available";
1022- return;
1023- }
1024-
1025- if (m_activationCount > 0)
1026- {
1027- // Update the GeoIp data again
1028- m_geoIp->start();
1029- }
1030-
1031- try
1032- {
1033- if (!m_session)
1034- {
1035- m_session = m_locationService->create_session_for_criteria(
1036- cul::Criteria());
1037-
1038- m_session->updates().position.changed().connect(
1039- bind(&UbuntuLocationService::Priv::positionChanged,
1040- this, _1));
1041- }
1042-
1043- if (m_activationCount > 0
1044- && m_session->updates().position_status
1045- == culss::Interface::Updates::Status::disabled)
1046- {
1047- qDebug() << "Enabling location updates";
1048- m_session->updates().position_status =
1049- culss::Interface::Updates::Status::enabled;
1050- m_geoipTimer.start();
1051- }
1052- else if (m_activationCount == 0
1053- && m_session->updates().position_status
1054- == culss::Interface::Updates::Status::enabled)
1055- {
1056- qDebug() << "Disabling location updates";
1057- m_session->updates().position_status =
1058- culss::Interface::Updates::Status::disabled;
1059- m_geoipTimer.stop();
1060- }
1061- }
1062- catch (exception& e)
1063- {
1064- qWarning() << e.what();
1065- }
1066- }
1067-
1068- void positionChanged(const cul::Update<cul::Position>& newPosition)
1069- {
1070- QMutexLocker lock(&m_lastLocationMutex);
1071-
1072- m_locationUpdatedAtLeastOnce = true;
1073- m_lastLocation = newPosition.value;
1074- Q_EMIT locationChanged();
1075- }
1076-
1077- void requestFinished(const GeoIp::Result& result)
1078- {
1079- QMutexLocker lock(&m_resultMutex);
1080- m_result = result;
1081- Q_EMIT locationChanged();
1082- }
1083-
1084- void activate()
1085- {
1086- ++m_activationCount;
1087- m_deactivateTimer.stop();
1088- update();
1089- }
1090-
1091- void deactivate()
1092- {
1093- --m_activationCount;
1094- if (m_activationCount < 0)
1095- {
1096- m_activationCount = 0;
1097- qWarning() << "Location service refcount error";
1098- }
1099- m_deactivateTimer.start();
1100- }
1101-
1102-public:
1103- dbus::Bus::Ptr m_bus;
1104-
1105- culs::Stub::Ptr m_locationService;
1106-
1107- culss::Interface::Ptr m_session;
1108-
1109- cul::Position m_lastLocation;
1110-
1111- QMutex m_lastLocationMutex;
1112-
1113- bool m_locationUpdatedAtLeastOnce = false;
1114-
1115- int m_activationCount = 0;
1116-
1117- QTimer m_geoipTimer;
1118-
1119- QTimer m_deactivateTimer;
1120-
1121- GeoIp::Ptr m_geoIp;
1122-
1123- QSharedPointer<QThread> m_dbusThread;
1124-
1125- QMutex m_resultMutex;
1126-
1127- GeoIp::Result m_result;
1128-};
1129-
1130-UbuntuLocationService::UbuntuLocationService(const GeoIp::Ptr& geoIp) :
1131- p(new Priv())
1132-{
1133- p->moveToThread(&m_thread);
1134-
1135+UbuntuLocationService::UbuntuLocationService(const GeoIp::Ptr& geoIp)
1136+ : m_geoIp(geoIp)
1137+{
1138 // If the location service is disabled
1139 if (qEnvironmentVariableIsSet("UNITY_SCOPES_NO_LOCATION"))
1140 {
1141 return;
1142 }
1143
1144- p->init(geoIp);
1145+ m_deactivateTimer.setInterval(DEACTIVATE_INTERVAL);
1146+ m_deactivateTimer.setSingleShot(true);
1147+ m_deactivateTimer.setTimerType(Qt::VeryCoarseTimer);
1148+
1149+ m_geoipTimer.setInterval(GEOIP_INTERVAL);
1150+ m_geoipTimer.setTimerType(Qt::CoarseTimer);
1151+
1152+ m_locationSource = QGeoPositionInfoSource::createDefaultSource(this);
1153+ connect(m_locationSource, &QGeoPositionInfoSource::positionUpdated, this, &UbuntuLocationService::positionChanged);
1154+ connect(m_locationSource, &QGeoPositionInfoSource::updateTimeout, this, &UbuntuLocationService::onPositionUpdateTimeout);
1155+ connect(m_locationSource, SIGNAL(error(QGeoPositionInfoSource::Error)), this, SLOT(onError(QGeoPositionInfoSource::Error)));
1156+
1157+ // Wire up the deactivate timer
1158+ connect(&m_deactivateTimer, &QTimer::timeout, this, &UbuntuLocationService::update);
1159+
1160+ // Wire up the network request finished timer
1161+ connect(m_geoIp.data(), &GeoIp::finished, this, &UbuntuLocationService::requestFinished);
1162+
1163+ // Wire up the GeoIP repeat timer
1164+ connect(&m_geoipTimer, &QTimer::timeout, m_geoIp.data(), &GeoIp::start);
1165
1166 // Connect to signals (which will be queued)
1167- connect(p.data(), &Priv::locationChanged, this, &LocationService::locationChanged, Qt::QueuedConnection);
1168- connect(this, &UbuntuLocationService::enqueueActivate, p.data(), &Priv::activate, Qt::QueuedConnection);
1169- connect(this, &UbuntuLocationService::enqueueDeactivate, p.data(), &Priv::deactivate, Qt::QueuedConnection);
1170-
1171- m_thread.start();
1172-}
1173-
1174-UbuntuLocationService::~UbuntuLocationService()
1175-{
1176- p.reset();
1177-
1178- m_thread.quit();
1179-
1180- if (m_thread.isRunning())
1181- {
1182- m_thread.wait();
1183- }
1184+ connect(this, &UbuntuLocationService::enqueueActivate, this, &UbuntuLocationService::doActivate, Qt::QueuedConnection);
1185+ connect(this, &UbuntuLocationService::enqueueDeactivate, this, &UbuntuLocationService::doDeactivate, Qt::QueuedConnection);
1186+
1187+ m_geoIp->start();
1188+}
1189+
1190+void UbuntuLocationService::doActivate()
1191+{
1192+ m_active = true;
1193+ ++m_activationCount;
1194+ m_deactivateTimer.stop();
1195+ update();
1196+}
1197+
1198+void UbuntuLocationService::doDeactivate()
1199+{
1200+ --m_activationCount;
1201+ if (m_activationCount < 0)
1202+ {
1203+ m_activationCount = 0;
1204+ qWarning() << "Location service refcount error";
1205+ }
1206+ m_deactivateTimer.start();
1207+}
1208+
1209+void UbuntuLocationService::update()
1210+{
1211+ if (m_activationCount > 0)
1212+ {
1213+ // Update the GeoIp data again
1214+ m_geoIp->start();
1215+ }
1216+
1217+ try
1218+ {
1219+ if (m_activationCount > 0)
1220+ {
1221+ qDebug() << "Enabling location updates";
1222+ m_locationSource->startUpdates();
1223+ m_geoipTimer.start();
1224+ }
1225+ else
1226+ {
1227+ qDebug() << "Disabling location updates";
1228+ m_active = false;
1229+ m_locationSource->stopUpdates();
1230+ m_geoipTimer.stop();
1231+ }
1232+ }
1233+ catch (exception& e)
1234+ {
1235+ qWarning() << e.what();
1236+ }
1237+}
1238+
1239+void UbuntuLocationService::positionChanged(const QGeoPositionInfo& update)
1240+{
1241+ m_locationUpdatedAtLeastOnce = true;
1242+ m_lastLocation = update;
1243+ Q_EMIT locationChanged();
1244+}
1245+
1246+void UbuntuLocationService::onPositionUpdateTimeout()
1247+{
1248+ qWarning() << "Position update timeout";
1249+ Q_EMIT locationTimeout();
1250+}
1251+
1252+void UbuntuLocationService::onError(QGeoPositionInfoSource::Error positioningError)
1253+{
1254+ qWarning() << "Position update error:" << positioningError;
1255+ if (positioningError == QGeoPositionInfoSource::AccessError) {
1256+ qDebug() << "Postion update denied";
1257+ Q_EMIT accessDenied();
1258+ }
1259+}
1260+
1261+void UbuntuLocationService::requestFinished(const GeoIp::Result& result)
1262+{
1263+ qDebug() << "GeoIP request finished";
1264+ {
1265+ m_result = result;
1266+ }
1267+ Q_EMIT geoIpLookupFinished();
1268 }
1269
1270 scopes::Location UbuntuLocationService::location() const
1271@@ -318,8 +183,7 @@
1272
1273 GeoIp::Result result;
1274 {
1275- QMutexLocker lock(&p->m_resultMutex);
1276- result = p->m_result;
1277+ result = m_result;
1278 }
1279
1280 if (result.valid)
1281@@ -336,28 +200,21 @@
1282 location.set_city(result.city.toStdString());
1283 }
1284
1285- QMutexLocker lock(&p->m_lastLocationMutex);
1286 // We need to be active, and the location session must have updated at least once
1287- if (isActive() && p->m_locationUpdatedAtLeastOnce)
1288+ if (isActive() && m_locationUpdatedAtLeastOnce)
1289 {
1290- cul::Position pos = p->m_lastLocation;
1291-
1292- if (pos.accuracy.horizontal)
1293- {
1294- location.set_horizontal_accuracy(pos.accuracy.horizontal.get().value());
1295- }
1296- if (pos.accuracy.vertical)
1297- {
1298- location.set_vertical_accuracy(pos.accuracy.vertical.get().value());
1299- }
1300-
1301- if (pos.altitude)
1302- {
1303- location.set_altitude(pos.altitude.get().value.value());
1304- }
1305-
1306- location.set_latitude(pos.latitude.value.value());
1307- location.set_longitude(pos.longitude.value.value());
1308+ location.set_latitude(m_lastLocation.coordinate().latitude());
1309+ location.set_longitude(m_lastLocation.coordinate().longitude());
1310+ location.set_altitude(m_lastLocation.coordinate().altitude());
1311+
1312+ if (m_lastLocation.hasAttribute(QGeoPositionInfo::HorizontalAccuracy))
1313+ {
1314+ location.set_horizontal_accuracy(m_lastLocation.attribute(QGeoPositionInfo::HorizontalAccuracy));
1315+ }
1316+ if (m_lastLocation.hasAttribute(QGeoPositionInfo::VerticalAccuracy))
1317+ {
1318+ location.set_vertical_accuracy(m_lastLocation.attribute(QGeoPositionInfo::VerticalAccuracy));
1319+ }
1320 }
1321 else if (result.valid)
1322 {
1323@@ -376,18 +233,24 @@
1324
1325 bool UbuntuLocationService::isActive() const
1326 {
1327- return p->m_session ? (p->m_session->updates().position_status ==
1328- culss::Interface::Updates::Status::enabled) : false;
1329+ return m_active;
1330 }
1331
1332 bool UbuntuLocationService::hasLocation() const
1333 {
1334- return p->m_result.valid || p->m_locationUpdatedAtLeastOnce;
1335+ return m_lastLocation.isValid() || m_locationUpdatedAtLeastOnce;
1336 }
1337
1338-QSharedPointer<LocationService::Token> UbuntuLocationService::activate()
1339+QSharedPointer<UbuntuLocationService::Token> UbuntuLocationService::activate()
1340 {
1341 return QSharedPointer<Token>(new TokenImpl(*this));
1342 }
1343
1344+void UbuntuLocationService::requestInitialLocation()
1345+{
1346+ qDebug() << "Requesting initial location update";
1347+ m_locationSource->requestUpdate();
1348+ m_geoipTimer.start();
1349+}
1350+
1351 #include "ubuntulocationservice.moc"
1352
1353=== modified file 'src/Unity/ubuntulocationservice.h'
1354--- src/Unity/ubuntulocationservice.h 2015-09-08 12:32:05 +0000
1355+++ src/Unity/ubuntulocationservice.h 2016-06-10 15:06:19 +0000
1356@@ -1,5 +1,5 @@
1357 /*
1358- * Copyright (C) 2014 Canonical, Ltd.
1359+ * Copyright (C) 2014-2016 Canonical, Ltd.
1360 *
1361 * This program is free software; you can redistribute it and/or modify
1362 * it under the terms of the GNU General Public License as published by
1363@@ -15,50 +15,80 @@
1364 *
1365 * Authors:
1366 * Pete Woods <pete.woods@canonical.com>
1367+ * Pawel Stolowski <pawel.stolowski@canonical.com>
1368 */
1369
1370 #ifndef UBUNTULOCATIONSERVICE_H
1371 #define UBUNTULOCATIONSERVICE_H
1372
1373 #include "geoip.h"
1374-#include "locationservice.h"
1375
1376+#include <QObject>
1377 #include <QSharedPointer>
1378-#include <QThread>
1379+#include <QTimer>
1380+#include <QGeoPositionInfo>
1381+#include <QGeoPositionInfoSource>
1382+#include <unity/scopes/Location.h>
1383
1384 namespace scopes_ng
1385 {
1386
1387-class Q_DECL_EXPORT UbuntuLocationService : public LocationService
1388+class Q_DECL_EXPORT UbuntuLocationService: public QObject
1389 {
1390 Q_OBJECT
1391+ Q_PROPERTY(unity::scopes::Location location READ location NOTIFY locationChanged)
1392+ Q_PROPERTY(bool active READ isActive NOTIFY activeChanged)
1393
1394 public:
1395+ typedef QSharedPointer<UbuntuLocationService> Ptr;
1396+ class Token : public QObject
1397+ {
1398+ };
1399+
1400 class TokenImpl;
1401
1402 UbuntuLocationService(const GeoIp::Ptr& geoIp = GeoIp::Ptr(new GeoIp));
1403-
1404- virtual ~UbuntuLocationService();
1405-
1406- unity::scopes::Location location() const override;
1407-
1408- bool hasLocation() const override;
1409-
1410- bool isActive() const override;
1411-
1412- QSharedPointer<Token> activate() override;
1413+ unity::scopes::Location location() const;
1414+ bool hasLocation() const;
1415+ bool isActive() const;
1416+ QSharedPointer<Token> activate();
1417+
1418+public Q_SLOTS:
1419+ void requestInitialLocation();
1420
1421 Q_SIGNALS:
1422+ // emited when location changes and only when access has been granted by apparmor
1423+ void locationChanged();
1424+
1425+ void locationTimeout();
1426+
1427+ // emited when geoip lookup finishes (including initial lookup on startup). regardless of apparmor permissions
1428+ // (receiving it doesn't mean position updates are allowed).
1429+ void geoIpLookupFinished();
1430+ void activeChanged();
1431+ void accessDenied();
1432 void enqueueActivate();
1433-
1434 void enqueueDeactivate();
1435
1436+protected Q_SLOTS:
1437+ void doActivate();
1438+ void doDeactivate();
1439+ void update();
1440+ void positionChanged(const QGeoPositionInfo& update);
1441+ void onPositionUpdateTimeout();
1442+ void onError(QGeoPositionInfoSource::Error positioningError);
1443+ void requestFinished(const GeoIp::Result& result);
1444+
1445 protected:
1446- class Priv;
1447-
1448- QThread m_thread;
1449-
1450- QSharedPointer<Priv> p;
1451+ bool m_active;
1452+ QGeoPositionInfoSource *m_locationSource;
1453+ QGeoPositionInfo m_lastLocation;
1454+ bool m_locationUpdatedAtLeastOnce = false;
1455+ int m_activationCount = 0;
1456+ QTimer m_geoipTimer;
1457+ QTimer m_deactivateTimer;
1458+ GeoIp::Ptr m_geoIp;
1459+ GeoIp::Result m_result;
1460 };
1461
1462 } // namespace scopes_ng
1463
1464=== modified file 'tests/CMakeLists.txt'
1465--- tests/CMakeLists.txt 2016-04-01 10:38:21 +0000
1466+++ tests/CMakeLists.txt 2016-06-10 15:06:19 +0000
1467@@ -7,7 +7,6 @@
1468 add_subdirectory(data)
1469
1470 add_definitions(
1471- -DGEOIP_SERVER_BINARY="${CMAKE_CURRENT_SOURCE_DIR}/geoip.ubuntu.com.py"
1472 -DTEST_DATA_DIR="${TEST_DATA_DIR}"
1473 -DTEST_RUNTIME_CONFIG="${TEST_RUNTIME_CONFIG}"
1474 -DTEST_SETTINGS_UNICODE="${CMAKE_CURRENT_SOURCE_DIR}/data/settings-unicode.ini"
1475@@ -18,7 +17,6 @@
1476 ${CMAKE_SOURCE_DIR}/src
1477 ${CMAKE_CURRENT_BINARY_DIR}
1478 ${SCOPESLIB_INCLUDE_DIRS}
1479- ${UBUNTU_LOCATION_SERVICE_INCLUDE_DIRS}
1480 ${QTDBUSTEST_INCLUDE_DIRS}
1481 ${QTDBUSMOCK_INCLUDE_DIRS}
1482 ${GSETTINGSQT_INCLUDE_DIRS}
1483@@ -65,7 +63,6 @@
1484 filtersendtoendtest
1485 optionselectorfiltertest
1486 favoritestest
1487- locationtest
1488 overviewtest
1489 previewtest
1490 resultstest
1491
1492=== removed file 'tests/geoip.ubuntu.com.py'
1493--- tests/geoip.ubuntu.com.py 2014-07-23 11:10:53 +0000
1494+++ tests/geoip.ubuntu.com.py 1970-01-01 00:00:00 +0000
1495@@ -1,66 +0,0 @@
1496-#!/usr/bin/env python
1497-# -*- coding: utf-8 -*-
1498-#
1499-# Copyright (C) 2014 Canonical Ltd
1500-#
1501-# This program is free software: you can redistribute it and/or modify
1502-# it under the terms of the GNU Lesser General Public License version 3 as
1503-# published by the Free Software Foundation.
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 Lesser General Public License for more details.
1509-#
1510-# You should have received a copy of the GNU Lesser General Public License
1511-# along with this program. If not, see <http://www.gnu.org/licenses/>.
1512-#
1513-# Authored by: Pete Woods <pete.woods@canonical.com>
1514-
1515-import tornado.httpserver
1516-import tornado.ioloop
1517-import tornado.netutil
1518-import tornado.web
1519-import sys
1520-
1521-RESPONSE = '''<Response>
1522-<Ip>1.2.3.4</Ip>
1523-<Status>OK</Status>
1524-<CountryCode>GB</CountryCode>
1525-<CountryCode3>GBR</CountryCode3>
1526-<CountryName>United Kingdom</CountryName>
1527-<RegionCode>H2</RegionCode>
1528-<RegionName>Lancashire</RegionName>
1529-<City>Accrington</City>
1530-<ZipPostalCode>BB5</ZipPostalCode>
1531-<Latitude>55.7654</Latitude>
1532-<Longitude>-2.7467</Longitude>
1533-<AreaCode>0</AreaCode>
1534-<TimeZone>Europe/London</TimeZone>
1535-</Response>
1536-'''
1537-
1538-class Lookup(tornado.web.RequestHandler):
1539- def get(self):
1540- sys.stderr.write('GeoIP location requested\n')
1541- sys.stderr.flush()
1542-
1543- self.write(RESPONSE)
1544- self.finish()
1545-
1546-def new_app():
1547- application = tornado.web.Application([
1548- (r"/lookup", Lookup),
1549- ], gzip=True)
1550- sockets = tornado.netutil.bind_sockets(0, '127.0.0.1')
1551- server = tornado.httpserver.HTTPServer(application)
1552- server.add_sockets(sockets)
1553-
1554- sys.stdout.write('%d\n' % sockets[0].getsockname()[1])
1555- sys.stdout.flush()
1556-
1557- return application
1558-
1559-if __name__ == "__main__":
1560- application = new_app()
1561- tornado.ioloop.IOLoop.instance().start()
1562
1563=== removed file 'tests/locationtest.cpp'
1564--- tests/locationtest.cpp 2015-02-26 18:02:32 +0000
1565+++ tests/locationtest.cpp 1970-01-01 00:00:00 +0000
1566@@ -1,234 +0,0 @@
1567-/*
1568- * Copyright (C) 2013-2014 Canonical, Ltd.
1569- *
1570- * This program is free software; you can redistribute it and/or modify
1571- * it under the terms of the GNU General Public License as published by
1572- * the Free Software Foundation; version 3.
1573- *
1574- * This program is distributed in the hope that it will be useful,
1575- * but WITHOUT ANY WARRANTY; without even the implied warranty of
1576- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1577- * GNU General Public License for more details.
1578- *
1579- * You should have received a copy of the GNU General Public License
1580- * along with this program. If not, see <http://www.gnu.org/licenses/>.
1581- *
1582- * Authors:
1583- * Pete Woods <pete.woods@canonical.com>
1584- */
1585-
1586-#include "ubuntulocationservice.h"
1587-
1588-#include <QProcess>
1589-#include <QSignalSpy>
1590-#include <QTest>
1591-#include <string>
1592-
1593-#include <libqtdbusmock/DBusMock.h>
1594-#include <libqtdbustest/DBusTestRunner.h>
1595-
1596-using namespace std;
1597-using namespace scopes_ng;
1598-using namespace unity::scopes;
1599-using namespace QtDBusTest;
1600-using namespace QtDBusMock;
1601-
1602-namespace
1603-{
1604-
1605-static const char* LOCATION_SERVICE_NAME = "com.ubuntu.location.Service";
1606-static const char* LOCATION_SERVICE_PATH = "/com/ubuntu/location/Service";
1607-static const char* LOCATION_SERVICE_INTERFACE = "com.ubuntu.location.Service";
1608-
1609-static const char* SESSION_NAME = "com.ubuntu.location.Service.Session";
1610-static const QString SESSION_PATH = "/com/ubuntu/location/session/%1";
1611-static const QString SESSION_INTERFACE = "com.ubuntu.location.Service.Session";
1612-
1613-static Variant geoip()
1614-{
1615- VariantMap result;
1616- result["area_code"] = "0";
1617- result["city"] = "Accrington";
1618- result["country_code"] = "GB";
1619- result["country_name"] = "United Kingdom";
1620- result["horizontal_accuracy"] = 100000.0;
1621- result["latitude"] = 55.76540;
1622- result["longitude"] = -2.74670;
1623- result["region_code"] = "H2";
1624- result["region_name"] = "Lancashire";
1625- result["zip_postal_code"] = "BB5";
1626- return Variant(result);
1627-}
1628-
1629-static Variant gps()
1630-{
1631- VariantMap result;
1632- result["altitude"] = 3.0;
1633- result["area_code"] = "0";
1634- result["city"] = "Accrington";
1635- result["country_code"] = "GB";
1636- result["country_name"] = "United Kingdom";
1637- result["horizontal_accuracy"] = 4.0;
1638- result["latitude"] = 1.0;
1639- result["longitude"] = 2.0;
1640- result["region_code"] = "H2";
1641- result["region_name"] = "Lancashire";
1642- result["vertical_accuracy"] = 5.0;
1643- result["zip_postal_code"] = "BB5";
1644- return Variant(result);
1645-}
1646-
1647-class LocationTest: public QObject
1648-{
1649-Q_OBJECT
1650-
1651-public:
1652- LocationTest() :
1653- mock(dbus)
1654- {
1655- }
1656-
1657-private:
1658- OrgFreedesktopDBusMockInterface& interface()
1659- {
1660- return mock.mockInterface(LOCATION_SERVICE_NAME, LOCATION_SERVICE_PATH,
1661- LOCATION_SERVICE_INTERFACE,
1662- QDBusConnection::SystemBus);
1663- }
1664-
1665- OrgFreedesktopDBusMockInterface& session(int id)
1666- {
1667- return mock.mockInterface(SESSION_NAME, SESSION_PATH.arg(id),
1668- SESSION_INTERFACE, QDBusConnection::SystemBus);
1669- }
1670-
1671- DBusTestRunner dbus;
1672-
1673- DBusMock mock;
1674-
1675- QScopedPointer<LocationService> locationService;
1676-
1677- QProcess geoIpServer;
1678-
1679- QUrl url;
1680-
1681-private Q_SLOTS:
1682- void initTestCase()
1683- {
1684- DBusMock::registerMetaTypes();
1685-
1686- // Register the main interface object
1687- mock.registerCustomMock(LOCATION_SERVICE_NAME, LOCATION_SERVICE_PATH,
1688- LOCATION_SERVICE_INTERFACE,
1689- QDBusConnection::SystemBus);
1690-
1691- // Register the first session object
1692- mock.registerCustomMock(SESSION_NAME, SESSION_PATH.arg(0),
1693- SESSION_INTERFACE,
1694- QDBusConnection::SystemBus);
1695-
1696- dbus.startServices();
1697-
1698- // Set up the main interface
1699- {
1700- interface().AddMethod(LOCATION_SERVICE_INTERFACE,
1701- "CreateSessionForCriteria", "bbbbdbbb", "o",
1702- "ret='/com/ubuntu/location/session/0'");
1703- }
1704-
1705- // Set up the first session
1706- {
1707- session(0).AddMethod(SESSION_INTERFACE, "StartPositionUpdates", "", "", "");
1708- session(0).AddMethod(SESSION_INTERFACE, "StopPositionUpdates", "", "", "");
1709- }
1710-
1711- geoIpServer.setProcessChannelMode(QProcess::ForwardedErrorChannel);
1712- geoIpServer.start(GEOIP_SERVER_BINARY);
1713- QVERIFY(geoIpServer.waitForStarted());
1714- QVERIFY(geoIpServer.waitForReadyRead());
1715-
1716- url = "http://127.0.0.1:" + geoIpServer.readAllStandardOutput().trimmed() + "/lookup";
1717- }
1718-
1719- void cleanupTestCase() {
1720- geoIpServer.terminate();
1721- QVERIFY(geoIpServer.waitForFinished());
1722- }
1723-
1724- void init()
1725- {
1726- locationService.reset(new UbuntuLocationService(GeoIp::Ptr(new GeoIp(url))));
1727- }
1728-
1729- void cleanup()
1730- {
1731- locationService.reset();
1732- }
1733-
1734- void compareVariant(const Variant& expected_in, const Variant& actual_in)
1735- {
1736- VariantMap expected(expected_in.get_dict());
1737- VariantMap actual(actual_in.get_dict());
1738-
1739- QVERIFY2(
1740- expected.size() <= actual.size(),
1741- qPrintable(QString("We need at least %1 entries, had %2").arg(
1742- expected.size()).arg(actual.size())));
1743- for(const auto entry: expected)
1744- {
1745- QVERIFY(actual.find(entry.first) != actual.end());
1746-
1747- Variant expectedVariant = entry.second;
1748- Variant actualVariant = actual[entry.first];
1749-
1750- if (expectedVariant.which() == Variant::Double)
1751- {
1752- bool comparison = qFuzzyCompare(expectedVariant.get_double(), actualVariant.get_double());
1753- if (!comparison)
1754- {
1755- qWarning() << "Comparison:" << expectedVariant.get_double() << "!=" << actualVariant.get_double();
1756- }
1757- QVERIFY(comparison);
1758- }
1759- else
1760- {
1761- QCOMPARE(expectedVariant, actualVariant);
1762- }
1763- }
1764- }
1765-
1766- void testLocation()
1767- {
1768- QSignalSpy spy(locationService.data(), SIGNAL(locationChanged()));
1769- auto token = locationService->activate();
1770-
1771- // The GeoIP HTTP call should return now
1772- QVERIFY(spy.wait());
1773- compareVariant(geoip(), Variant(locationService->location().serialize()));
1774-
1775- // Call the object that the location service client creates
1776- QDBusInterface remoteObject(":1.4", SESSION_PATH.arg(0), SESSION_INTERFACE,
1777- QDBusConnection::systemBus());
1778-
1779- QString errorMessage("never called");
1780- for (int i = 0; i < 10 && !errorMessage.isEmpty(); ++i)
1781- {
1782- QDBusMessage reply = remoteObject.callWithArgumentList(
1783- QDBus::Block,
1784- "UpdatePosition",
1785- QVariantList() << 1.0 << 2.0 << true << 3.0 << true << 4.0
1786- << true << 5.0 << qint64(1234));
1787- errorMessage = reply.errorMessage();
1788- }
1789- QCOMPARE(QString(), errorMessage);
1790-
1791- // The GPS update should return now
1792- QVERIFY(spy.wait());
1793- QTRY_COMPARE((unsigned int) locationService->location().serialize().size(), 12u);
1794- compareVariant(gps(), Variant(locationService->location().serialize()));
1795- }
1796-};
1797-
1798-}
1799-QTEST_GUILESS_MAIN(LocationTest)
1800-#include <locationtest.moc>

Subscribers

People subscribed via source and target branches

to all changes: