Merge lp:~nik90/ubuntu-clock-app/clock-code-restructure into lp:ubuntu-clock-app/saucy

Proposed by Nekhelesh Ramananthan
Status: Merged
Approved by: Nekhelesh Ramananthan
Approved revision: 261
Merged at revision: 247
Proposed branch: lp:~nik90/ubuntu-clock-app/clock-code-restructure
Merge into: lp:ubuntu-clock-app/saucy
Diff against target: 1125 lines (+518/-417)
11 files modified
clock/AnalogClockFace.qml (+33/-6)
clock/CityTimezone.qml (+53/-0)
clock/ClockPage.qml (+11/-30)
clock/EasterEgg.qml (+8/-82)
clock/EasterEggModel.qml (+41/-0)
clock/EasterEggStorage.js (+76/-0)
clock/SearchBox.qml (+65/-0)
clock/WorldCity.qml (+57/-0)
clock/WorldClock.qml (+173/-222)
clock/WorldPage.qml (+0/-76)
tests/autopilot/ubuntu_clock_app/emulators.py (+1/-1)
To merge this branch: bzr merge lp:~nik90/ubuntu-clock-app/clock-code-restructure
Reviewer Review Type Date Requested Status
Ubuntu Phone Apps Jenkins Bot continuous-integration Approve
Ubuntu Clock Developers Pending
Review via email: mp+191912@code.launchpad.net

Commit message

This commit performs some code restructuring and organises elements into separate components.

- Moved world clock search box, worldcity xml model and citytimezone xml model to its own component
- Split EasterEgg.qml into EasterEggModel.qml, EasterEggStorage.js and EasterEgg.qml
- Merged WorldClock.qml and WorldPage.qml
- Removed inverse_mouse area (not required)
- Removed search button
- Added TODO, FIXME comments. Increased code clarity and made it easier for new developers to get involved (clock).

This commit should make it easier to fix any world clock related bugs and is a nice pre-runner to https://bugs.launchpad.net/bugs/1233986

Description of the change

In the rush to 1.0 release, the code dealing with clock alone has become messy and complexy interlinked. This MP performs some code restructuring and organises elements into separate components.

- Moved world clock search box, worldcity xml model and citytimezone xml model to its own component
- Split EasterEgg.qml into EasterEggModel.qml, EasterEggStorage.js and EasterEgg.qml
- Merged WorldClock.qml and WorldPage.qml
- Removed inverse_mouse area (not required)
- Removed search button
- Added TODO, FIXME comments. Increased code clarity and made it easier for new developers to get involved (clock).

This MP should make it easier to fix any world clock related bugs and is a nice pre-runner to https://bugs.launchpad.net/bugs/1233986

To post a comment you must log in.
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
259. By Nekhelesh Ramananthan

Fixed autopilot test

260. By Nekhelesh Ramananthan

removed username variable

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
261. By Nekhelesh Ramananthan

Merge from master

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'clock/AnalogClockFace.qml'
--- clock/AnalogClockFace.qml 2013-07-14 12:33:25 +0000
+++ clock/AnalogClockFace.qml 2013-10-22 17:11:13 +0000
@@ -22,13 +22,16 @@
22import Ubuntu.Components 0.122import Ubuntu.Components 0.1
23import "../common"23import "../common"
2424
25// Element which draws the clock face along with the hour, minute, second hand and the clock label
25AnalogFaceBase {26AnalogFaceBase {
26 id: clockRoot27 id: clockRoot
2728
28 property var currDate: new Date;29 property var currDate: new Date;
29 property int hours: currDate.getHours()30 property int hours: currDate.getHours()
30 property int minutes: currDate.getMinutes()31 property int minutes: currDate.getMinutes()
31 property int seconds: currDate.getUTCSeconds(); 32 property int seconds: currDate.getUTCSeconds();
33
34 property alias clockLabel: currentTimeLabel
3235
33 signal clicked(var mouse)36 signal clicked(var mouse)
3437
@@ -43,7 +46,7 @@
4346
44 z: parent.z47 z: parent.z
45 handHeight: units.gu(12.5); handWidth: units.gu(1);48 handHeight: units.gu(12.5); handWidth: units.gu(1);
46 rotation: (clockRoot.hours * 30) + (clockRoot.minutes / 2);49 rotation: (hours * 30) + (minutes / 2);
47 }50 }
4851
49 AnalogClockHand {52 AnalogClockHand {
@@ -51,7 +54,7 @@
5154
52 z: parent.z + 155 z: parent.z + 1
53 handHeight: units.gu(14.5); handWidth: units.gu(0.5);56 handHeight: units.gu(14.5); handWidth: units.gu(0.5);
54 rotation: clockRoot.minutes * 6;57 rotation: minutes * 6;
55 }58 }
5659
57 AnalogClockHand {60 AnalogClockHand {
@@ -59,9 +62,33 @@
5962
60 z: parent.z - 1;63 z: parent.z - 1;
61 handHeight: units.gu(17); handWidth: units.gu(0.5)64 handHeight: units.gu(17); handWidth: units.gu(0.5)
62 rotation: clockRoot.seconds * 6;65 rotation: seconds * 6;
63 } 66 }
6467
68 // Label to show the current time
69 // TODO: Investigate moving this component into common/AnalogFaceBase.qml since it is required by
70 // clock, timer, stopwatch and alarm.
71 Rectangle {
72 id: labelContainer;
73
74 z: innerCircle.z + 1;
75 width: innerCircle.width; height: width;
76 anchors.centerIn: parent;
77 color: "transparent"
78
79 Label {
80 id: currentTimeLabel
81 objectName: "currentTimeLabel"
82
83 anchors.centerIn: parent
84 horizontalAlignment: Text.AlignHCenter
85 verticalAlignment: Text.AlignVCenter
86 color: Theme.palette.normal.baseText
87 font.pixelSize: units.dp(41)
88 }
89 }
90
91 // Mouse area to reveal the clock sunrise/sunset on clicking the clock face
65 MouseArea {92 MouseArea {
66 anchors.fill: parent93 anchors.fill: parent
67 z: parent.z + 1;94 z: parent.z + 1;
6895
=== added file 'clock/CityTimezone.qml'
--- clock/CityTimezone.qml 1970-01-01 00:00:00 +0000
+++ clock/CityTimezone.qml 2013-10-22 17:11:13 +0000
@@ -0,0 +1,53 @@
1/*
2 * Copyright (C) 2013 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Nekhelesh Ramananthan <krnekhelesh@gmail.com>
17 */
18
19import QtQuick 2.0
20import Ubuntu.Components 0.1
21import QtQuick.XmlListModel 2.0
22
23// XML Model to retrieve the timezone info of a city searched by the user online.
24XmlListModel {
25 id: cityTimezoneModel;
26
27 readonly property string timezone_base_url: "http://api.geonames.org/timezone?lat=";
28
29 // TODO: Change username to something along the lines of "com.ubuntu.clock"
30 readonly property string username: "krnekhelesh";
31
32 // Function to return the online url for timezone info of a city
33 function getCityTimezoneUrl (lat, lng) {
34 return (timezone_base_url + lat + "&lng=" + lng + "&username=" + username);
35 }
36
37 // Function to calculate the time difference w.r.t UTC
38 function getTimeDifference(data) {
39 var worldTime = data.split(' ')[1].split(':');
40 var hoursWT = parseInt(worldTime[0]);
41 var minutesWT = parseInt(worldTime[1]);
42 var now = new Date();
43 var UTCdiff = new Date().getTimezoneOffset()
44 now.setMinutes(now.getMinutes() + UTCdiff)
45
46 // Calculating world city time diff w.r.t UTC
47 return ((hoursWT - now.getHours()) * 60 + (minutesWT - now.getMinutes()));
48 }
49
50 query: "/geonames/timezone"
51 XmlRole { name: "time"; query: "time/string()" }
52 onSourceChanged: reload();
53}
054
=== modified file 'clock/ClockPage.qml'
--- clock/ClockPage.qml 2013-10-06 12:13:35 +0000
+++ clock/ClockPage.qml 2013-10-22 17:11:13 +0000
@@ -38,7 +38,7 @@
3838
39 Component.onCompleted: {39 Component.onCompleted: {
40 // TODO: Query GPS for location data40 // TODO: Query GPS for location data
41 Utils.log("ClockPage loaded"); 41 Utils.log("ClockPage loaded");
42 if (worldModel.city !== "undefined") {42 if (worldModel.city !== "undefined") {
43 currentCity = worldModel.city;43 currentCity = worldModel.city;
44 currentLng = worldModel.longitude;44 currentLng = worldModel.longitude;
@@ -58,18 +58,20 @@
58 keywords: i18n.tr("Add;Timezone;Timezones;World;City;Cities;Town;Towns;Place;Places;Location;Locations;Time;Locale;Local;Current")58 keywords: i18n.tr("Add;Timezone;Timezones;World;City;Cities;Town;Towns;Place;Places;Location;Locations;Time;Locale;Local;Current")
59 description: "Add a world city"59 description: "Add a world city"
60 iconSource: Qt.resolvedUrl("../images/add_icon.png")60 iconSource: Qt.resolvedUrl("../images/add_icon.png")
61 onTriggered: pagestack.push(Qt.resolvedUrl("WorldPage.qml"), {"isWorldCity": true})61 onTriggered: pagestack.push(Qt.resolvedUrl("WorldClock.qml"), {"isWorldCity": true})
62 }62 }
63 ]63 ]
6464
65 // Function which runs every second to update the clock label
65 function onTimerUpdate(now) {66 function onTimerUpdate(now) {
66 currentTimeFormatted = Qt.formatTime(new Date(), "hh:mm") 67 currentTimeFormatted = Qt.formatTime(new Date(), "hh:mm")
67 clockFace.timeChanged(now)68 clockFace.timeChanged(now)
68 if (now.getUTCSeconds() == 0) {69 if (now.getUTCSeconds() == 0) {
69 updateTime();70 updateTime();
70 }71 }
71 }72 }
7273
74 // Function which runs every minute to update the world time
73 function updateTime() {75 function updateTime() {
74 var now = new Date();76 var now = new Date();
75 now.setMinutes(now.getMinutes() + diff)77 now.setMinutes(now.getMinutes() + diff)
@@ -111,40 +113,19 @@
111 contentWidth: parent.width113 contentWidth: parent.width
112 contentHeight: clockFace.height + clockFace.anchors.topMargin + savedWorldClock.height + savedWorldClock.anchors.topMargin + units.gu(3)114 contentHeight: clockFace.height + clockFace.anchors.topMargin + savedWorldClock.height + savedWorldClock.anchors.topMargin + units.gu(3)
113115
114 // Label to show the current time
115 Rectangle {
116 id: labelContainer;
117
118 z: clockFace.z + 1;
119 width: clockFace.innerCircle.width; height: width;
120 anchors.centerIn: clockFace;
121 color: "transparent"
122
123 Label {
124 id: currentTimeLabel
125 objectName: "currentTimeLabel"
126
127 anchors.centerIn: parent
128 horizontalAlignment: Text.AlignHCenter
129 verticalAlignment: Text.AlignVCenter
130 color: Theme.palette.normal.baseText
131 font.pixelSize: units.dp(41)
132 text: currentTimeFormatted
133 }
134 }
135
136 // Qml Element to draw the analogue clock face along with its hour, minute and second hands.116 // Qml Element to draw the analogue clock face along with its hour, minute and second hands.
137 AnalogClockFace {117 AnalogClockFace {
138 id: clockFace118 id: clockFace
139119
140 anchors { top: parent.top; topMargin: units.gu(10); horizontalCenter: parent.horizontalCenter }120 anchors { top: parent.top; topMargin: units.gu(10); horizontalCenter: parent.horizontalCenter }
121 clockLabel.text: currentTimeFormatted
141122
142 onClicked: {123 onClicked: {
143 if (easterEggCircle.isReady == XmlListModel.Ready && worldModel.city !== "undefined") {124 if (easterEggCircle.isReady == XmlListModel.Ready && worldModel.city !== "undefined")
144 clockFace.state == "" ? clockFace.state = "easteregg" : clockFace.state = "";125 clockFace.state == "" ? clockFace.state = "easteregg" : clockFace.state = "";
145 } else {126 else
146 Utils.error("Sunrise/Sunset times cannot be loaded without setting the current location or without a internet connection.")127 Utils.error("Sunrise/Sunset times cannot be loaded without setting the current location or without a internet connection.")
147 }128
148 }129 }
149130
150 EasterEgg {131 EasterEgg {
@@ -158,13 +139,13 @@
158 State {139 State {
159 name: "easteregg"140 name: "easteregg"
160 PropertyChanges { target: easterEggCircle; opacity: 1; }141 PropertyChanges { target: easterEggCircle; opacity: 1; }
161 PropertyChanges { target: currentTimeLabel; opacity: 0; }142 PropertyChanges { target: clockFace.clockLabel; opacity: 0; }
162 PropertyChanges { target: easterEggCircle; sunriseLabel: easterEggCircle.getSunTime(easterEggCircle.model.get(0).sunriseTime); }143 PropertyChanges { target: easterEggCircle; sunriseLabel: easterEggCircle.getSunTime(easterEggCircle.model.get(0).sunriseTime); }
163 PropertyChanges { target: easterEggCircle; sunsetLabel: easterEggCircle.getSunTime(easterEggCircle.model.get(0).sunsetTime); }144 PropertyChanges { target: easterEggCircle; sunsetLabel: easterEggCircle.getSunTime(easterEggCircle.model.get(0).sunsetTime); }
164 },145 },
165 State {146 State {
166 name: ""147 name: ""
167 PropertyChanges { target: currentTimeLabel; opacity: 1; }148 PropertyChanges { target: clockFace.clockLabel; opacity: 1; }
168 PropertyChanges { target: easterEggCircle; opacity: 0; }149 PropertyChanges { target: easterEggCircle; opacity: 0; }
169 }150 }
170 ]151 ]
171152
=== modified file 'clock/EasterEgg.qml'
--- clock/EasterEgg.qml 2013-10-08 13:01:27 +0000
+++ clock/EasterEgg.qml 2013-10-22 17:11:13 +0000
@@ -21,70 +21,13 @@
21import Ubuntu.Components 0.121import Ubuntu.Components 0.1
22import QtQuick.XmlListModel 2.022import QtQuick.XmlListModel 2.0
23import QtQuick.LocalStorage 2.023import QtQuick.LocalStorage 2.0
2424import "EasterEggStorage.js" as Storage
25import "../common/ClockUtils.js" as Utils25import "../common/ClockUtils.js" as Utils
2626
27// Qml Element to draw the easter egg (sunrise and sunset) on the analogue clock face.27// Qml Element to draw the easter egg (sunrise and sunset) on the analogue clock face.
28Rectangle {28Rectangle {
29 id: easterEggCircle;29 id: easterEggCircle;
3030
31 property var db: null
32
33 // Opens the local db if not already open. On the first call creates the table
34 function openDB () {
35 if(db !== null) return;
36
37 db = LocalStorage.openDatabaseSync("ubuntu-clock-app", "", "Sunrise and Sunset Database", 1000);
38
39 try {
40 db.transaction(function(tx){
41 tx.executeSql('CREATE TABLE IF NOT EXISTS Easteregg(key INTEGER PRIMARY KEY, date TEXT, riseTime TEXT, setTime TEXT)');
42 var table = tx.executeSql("SELECT * FROM Easteregg");
43 if (table.rows.length == 0) {
44 tx.executeSql('INSERT INTO Easteregg VALUES(?, ?, ?, ?)', [1, '', '', '']);
45 };
46 });
47 } catch (err) {
48 Utils.error("Error creating table in Sunrise/Sunset database: " + err);
49 };
50 }
51
52 //Read date last checked from DB
53 function getSavedData () {
54
55 openDB();
56
57 var result = new Array();
58 try {
59 db.readTransaction(
60 function(tx){
61 var res = tx.executeSql('SELECT * FROM Easteregg WHERE key = 1');
62 if (res.rows.length > 0) {
63 result[0] = new Array();
64 result[0][0] = res.rows.item(0).key;
65 result[0][1] = res.rows.item(0).date;
66 result[0][2] = res.rows.item(0).riseTime;
67 result[0][3] = res.rows.item(0).setTime;
68 };
69 });
70 } catch (err) {
71 Utils.error("Error getting sunrise/sunset last checked date: " + err);
72 }
73 return result;
74 }
75
76 function saveSunTimes (date, sunRise, sunSet) {
77 openDB();
78 try {
79 db.transaction(function(tx){
80 var res = tx.executeSql('UPDATE Easteregg SET date = ?, riseTime = ?, setTime = ? WHERE key = 1', [date, sunRise, sunSet]);
81 Utils.log("Saving Sunrise/Sunset times to disk");
82 });
83 } catch (err) {
84 Utils.error("Error saving sunrise/sunset times: " + err);
85 };
86 }
87
88 // FIXME: Replace these constant values with user's location coordinates when automatic location detection is implemented.31 // FIXME: Replace these constant values with user's location coordinates when automatic location detection is implemented.
89 property real latitude: 200;32 property real latitude: 200;
90 property real longitude: 200;33 property real longitude: 200;
@@ -95,49 +38,32 @@
9538
96 // properties to get the sunRiseModel XML model.39 // properties to get the sunRiseModel XML model.
97 property alias isReady: sunRiseModel.status;40 property alias isReady: sunRiseModel.status;
98 property alias model: sunRiseModel;41 property alias model: sunRiseModel;
99
100 // Online API properties
101 readonly property string base_url: "http://api.geonames.org/timezone?";
102 readonly property string username: "krnekhelesh";
10342
104 opacity: 043 opacity: 0
105 color: "transparent"44 color: "transparent"
106 z: parent.z + 10045 z: parent.z + 100
107 visible: opacity == 0 ? false : true46 visible: opacity == 0 ? false : true
10847
109 function reloadSunRiseModel () {
110 sunRiseModel.reload()
111 }
112
113 // Function to form the url string to make the API call
114 function getUrlString (latitude, longitude) {
115 return (base_url + "lat=" + latitude + "&lng=" + longitude + "&username=" + username);
116 }
117
118 // Function to convert online data format "2013-04-20 19:40" to just "19:40"48 // Function to convert online data format "2013-04-20 19:40" to just "19:40"
119 function getSunTime (data) {49 function getSunTime (data) {
120 return data.split(' ')[1];50 return data.split(' ')[1];
121 }51 }
12252
53 onLatitudeChanged: sunRiseModel.source = sunRiseModel.getUrlString(latitude, longitude)
54 onLongitudeChanged: sunRiseModel.source = sunRiseModel.getUrlString(latitude, longitude)
55
123 // TODO 1: Get the latitude and longitude using the platform API automatically instead of using static values. 56 // TODO 1: Get the latitude and longitude using the platform API automatically instead of using static values.
124 XmlListModel {57 EasterEggModel {
125 id: sunRiseModel58 id: sunRiseModel
126
127 source: getUrlString(latitude, longitude)
128 query: "/geonames/timezone"
129
130 XmlRole { name: "sunriseTime"; query: "sunrise/string()"; isKey: true }
131 XmlRole { name: "sunsetTime"; query: "sunset/string()"; isKey: true }
132
133 onStatusChanged: {59 onStatusChanged: {
134 var savedData = getSavedData();60 var savedData = Storage.getSavedData();
135 var checkedDate = savedData[0][1].split('T')[0]61 var checkedDate = savedData[0][1].split('T')[0]
136 var date = new Date().toISOString();62 var date = new Date().toISOString();
137 if (status == XmlListModel.Ready && checkedDate != date.split('T')[0] && longitude != 200 && latitude != 200){63 if (status == XmlListModel.Ready && checkedDate != date.split('T')[0] && longitude != 200 && latitude != 200){
138 sunriseTimeLabel.text = getSunTime(sunRiseModel.get(0).sunriseTime);64 sunriseTimeLabel.text = getSunTime(sunRiseModel.get(0).sunriseTime);
139 sunsetTimeLabel.text = getSunTime(sunRiseModel.get(0).sunsetTime);65 sunsetTimeLabel.text = getSunTime(sunRiseModel.get(0).sunsetTime);
140 saveSunTimes(date, sunriseTimeLabel.text, sunsetTimeLabel.text);66 Storage.saveSunTimes(date, sunriseTimeLabel.text, sunsetTimeLabel.text);
141 }67 }
142 else if (status == XmlListModel.Ready) {68 else if (status == XmlListModel.Ready) {
143 Utils.log("Retrieving Sunrise/Sunset times from disk");69 Utils.log("Retrieving Sunrise/Sunset times from disk");
14470
=== added file 'clock/EasterEggModel.qml'
--- clock/EasterEggModel.qml 1970-01-01 00:00:00 +0000
+++ clock/EasterEggModel.qml 2013-10-22 17:11:13 +0000
@@ -0,0 +1,41 @@
1/*
2 * Copyright (C) 2013 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Nekhelesh Ramananthan <krnekhelesh@gmail.com>
17 */
18
19import QtQuick 2.0
20import Ubuntu.Components 0.1
21import QtQuick.XmlListModel 2.0
22
23// Qml Element to draw the easter egg (sunrise and sunset) on the analogue clock face.
24XmlListModel {
25 id: sunRiseModel
26
27 // Online API properties
28 readonly property string base_url: "http://api.geonames.org/timezone?";
29 readonly property string username: "krnekhelesh";
30
31 // Function to form the url string to make the API call
32 function getUrlString (latitude, longitude) {
33 return (base_url + "lat=" + latitude + "&lng=" + longitude + "&username=" + username);
34 }
35
36 query: "/geonames/timezone"
37 onSourceChanged: reload()
38
39 XmlRole { name: "sunriseTime"; query: "sunrise/string()"; isKey: true }
40 XmlRole { name: "sunsetTime"; query: "sunset/string()"; isKey: true }
41}
042
=== added file 'clock/EasterEggStorage.js'
--- clock/EasterEggStorage.js 1970-01-01 00:00:00 +0000
+++ clock/EasterEggStorage.js 2013-10-22 17:11:13 +0000
@@ -0,0 +1,76 @@
1/*
2 * Copyright (C) 2013 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Nekhelesh Ramananthan <krnekhelesh@gmail.com>
17 */
18
19Qt.include("../common/ClockUtils.js")
20
21var db = null;
22
23// Opens the local db if not already open. On the first call creates the table
24function openDB () {
25 if(db !== null) return;
26
27 db = LocalStorage.openDatabaseSync("ubuntu-clock-app", "", "Sunrise and Sunset Database", 1000);
28
29 try {
30 db.transaction(function(tx){
31 tx.executeSql('CREATE TABLE IF NOT EXISTS Easteregg(key INTEGER PRIMARY KEY, date TEXT, riseTime TEXT, setTime TEXT)');
32 var table = tx.executeSql("SELECT * FROM Easteregg");
33 if (table.rows.length == 0) {
34 tx.executeSql('INSERT INTO Easteregg VALUES(?, ?, ?, ?)', [1, '', '', '']);
35 };
36 });
37 } catch (err) {
38 console.error("Error creating table in Sunrise/Sunset database: " + err);
39 };
40}
41
42//Read date last checked from DB
43function getSavedData () {
44
45 openDB();
46
47 var result = new Array();
48 try {
49 db.readTransaction(
50 function(tx){
51 var res = tx.executeSql('SELECT * FROM Easteregg WHERE key = 1');
52 if (res.rows.length > 0) {
53 result[0] = new Array();
54 result[0][0] = res.rows.item(0).key;
55 result[0][1] = res.rows.item(0).date;
56 result[0][2] = res.rows.item(0).riseTime;
57 result[0][3] = res.rows.item(0).setTime;
58 };
59 });
60 } catch (err) {
61 console.error("Error getting sunrise/sunset last checked date: " + err);
62 }
63 return result;
64}
65
66function saveSunTimes (date, sunRise, sunSet) {
67 openDB();
68 try {
69 db.transaction(function(tx){
70 var res = tx.executeSql('UPDATE Easteregg SET date = ?, riseTime = ?, setTime = ? WHERE key = 1', [date, sunRise, sunSet]);
71 console.log("Saving Sunrise/Sunset times to disk");
72 });
73 } catch (err) {
74 console.error("Error saving sunrise/sunset times: " + err);
75 };
76}
077
=== added file 'clock/SearchBox.qml'
--- clock/SearchBox.qml 1970-01-01 00:00:00 +0000
+++ clock/SearchBox.qml 2013-10-22 17:11:13 +0000
@@ -0,0 +1,65 @@
1/*
2 * Copyright (C) 2013 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Nekhelesh Ramananthan <krnekhelesh@gmail.com>
17 */
18
19import QtQuick 2.0
20import Ubuntu.Components 0.1
21import QtQuick.XmlListModel 2.0
22
23// Search Box element for world clock. Instant search as you type after a small pause.
24Item {
25 id: searchBox;
26
27 // Property to hold the string inputted by the user. This is required in other places such as the XML Model for initiating an online query.
28 property alias search_string: searchLabel.text
29
30 // Signal connected to the search_timer triggered signal to expose it to outside objects
31 signal searchTriggered()
32
33 Component.onCompleted: search_timer.triggered.connect(searchTriggered)
34
35 // Search Label
36 TextField {
37 id: searchLabel
38
39 anchors { left: parent.left; leftMargin: units.gu(2); top: parent.top; bottom: parent.bottom; right: parent.right; rightMargin: units.gu(2) }
40 width: parent.width/1.5
41 hasClearButton: true
42 placeholderText: i18n.tr("Search")
43 primaryItem: Image {
44 height: parent.height/1.5;
45 fillMode: Image.PreserveAspectFit
46 source: Qt.resolvedUrl("../images/search_icon.svg")
47 }
48
49 // Provide a small pause before going online to search
50 Timer {
51 id: search_timer
52 interval: 1100
53 repeat: false
54 }
55
56 onTextChanged: search_timer.restart()
57 }
58
59 // Indicator to show search activity
60 ActivityIndicator {
61 id: searchActivity
62 anchors { verticalCenter: searchLabel.verticalCenter; right: searchLabel.right; rightMargin: units.gu(1) }
63 running: searchCityModel.status === XmlListModel.Loading
64 }
65}
066
=== added file 'clock/WorldCity.qml'
--- clock/WorldCity.qml 1970-01-01 00:00:00 +0000
+++ clock/WorldCity.qml 2013-10-22 17:11:13 +0000
@@ -0,0 +1,57 @@
1/*
2 * Copyright (C) 2013 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Nekhelesh Ramananthan <krnekhelesh@gmail.com>
17 */
18
19import QtQuick 2.0
20import Ubuntu.Components 0.1
21import QtQuick.XmlListModel 2.0
22
23// XML Model to list cities provided locally by clock-app or search online (geoname.org) for more cities and return latitude and longitude information of the city
24XmlListModel {
25 id: worldCityModel;
26
27 readonly property string search_base_url: "http://api.geonames.org/search?q="
28 readonly property string local_base_url: "common-cities.xml"
29 readonly property int no_of_results: 5
30 property string search_string: ""
31
32 // TODO: Change username to something along the lines of "com.ubuntu.clock"
33 readonly property string username: "krnekhelesh";
34
35 // Function to return the online search url for city search
36 function searchCityUrl(city) {
37 return (search_base_url + search_string + "&maxRows=" + no_of_results + "&username=" + username + "&style=full&featureClass=P&name_startsWith=" + city);
38 }
39
40 // Function to return the url of the locally provided city list (xml file)
41 function localCityUrl() {
42 return Qt.resolvedUrl(local_base_url);
43 }
44
45 // By default list the cities provided locally by clock app
46 source: localCityUrl()
47 query: "/geonames/geoname"
48
49 XmlRole { name: "city"; query: "toponymName/string()"; isKey: true }
50 XmlRole { name: "country"; query: "countryName/string()"; isKey: true }
51 XmlRole { name: "lat"; query: "lat/string()"; isKey: true }
52 XmlRole { name: "lng"; query: "lng/string()"; isKey: true }
53 XmlRole { name: "adminName"; query: "adminName1/string()"; isKey: true}
54 XmlRole { name: "adminName2"; query: "adminName2/string()"; isKey: true}
55
56 onSourceChanged: reload();
57}
058
=== modified file 'clock/WorldClock.qml'
--- clock/WorldClock.qml 2013-10-08 22:52:13 +0000
+++ clock/WorldClock.qml 2013-10-22 17:11:13 +0000
@@ -1,230 +1,181 @@
1/*
2 * Copyright (C) 2013 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Nekhelesh Ramananthan <krnekhelesh@gmail.com>
17 */
18
1import QtQuick 2.019import QtQuick 2.0
2import Ubuntu.Components 0.120import Ubuntu.Components 0.1
3import Ubuntu.Components.ListItems 0.1 as ListItem21import Ubuntu.Components.ListItems 0.1 as ListItem
4import QtQuick.XmlListModel 2.022import QtQuick.XmlListModel 2.0
523
6Column {24// Page to to search a world city and add it to the world clocks
7 id: worldClocks25Page {
826 id: worldPage
9 // Properties to store the user searched city details27
10 property string city: "null";28 property bool isWorldCity;
11 property real lng: 0;29
12 property real lat: 0;30 visible: false;
13 property real rawOffSet: 0;31 title: isWorldCity ? i18n.tr("Add City") : i18n.tr("Edit Current Location")
1432
15 // Property to store the city searched by the user33 Column {
16 property string search_string: "null";34 id: worldClocks
1735
18 // Properties to store the information related to api.geonames.org36 // Properties to store the user searched city details
19 readonly property string username: "krnekhelesh";37 property string city: "null";
20 readonly property string search_base_url: "http://api.geonames.org/search?q=";38 property real lng: 0;
21 readonly property string timezone_base_url: "http://api.geonames.org/timezone?lat=";39 property real lat: 0;
22 readonly property string local_base_url: "common-cities.xml";40
23 readonly property int no_of_results: 5;41 // Function to clear user search and return to original state
2442 function clearUserSearch () {
25 property alias searchModel: searchCityModel;43 searchCityModel.source = searchCityModel.localCityUrl();
26 property alias index: worldList.currentIndex;44 searchBox.search_string = "";
2745 worldList.currentIndex = -1;
28 signal clicked()46 }
2947
30 function searchCityUrl (city) {48 anchors { fill: parent; topMargin: units.gu(2) }
31 return (search_base_url + search_string + "&maxRows=" + no_of_results + "&username=" + username + "&style=full&featureClass=P&name_startsWith=" + city);49 height: childrenRect.height;
32 }50 spacing: units.gu(2);
3351
34 function localCityUrl () {52 // XML Model to list cities provided locally with clock-app or search cities online and retrieve latitude and longitude of searched city
35 return Qt.resolvedUrl(local_base_url);53 WorldCity {
36 }54 id: searchCityModel
3755 search_string: searchBox.search_string
38 function getCityTimezoneUrl (lat, lng) {56 }
39 return (timezone_base_url + lat + "&lng=" + lng + "&username=" + username);57
40 }58 // XML Model to retrieve timezone of searched city
4159 CityTimezone {
42 function clearUserSearch () {60 id: cityDetailsModel
43 searchCityModel.source = localCityUrl();61 onStatusChanged: {
44 searchLabel.text = "";62 if(status == XmlListModel.Ready && worldClocks.city != "null") {
45 worldList.currentIndex = -1;63 if (isWorldCity)
46 }64 worldModel.appendPreset(worldClocks.city, getTimeDifference(cityDetailsModel.get(0).time), worldClocks.lng, worldClocks.lat);
4765 else
48 height: childrenRect.height;66 worldModel.appendCurrentLocation(worldClocks.city, worldClocks.lng, worldClocks.lat);
49 spacing: units.gu(2);67 worldClocks.clearUserSearch();
5068 pageStack.pop()
51 // Xml model to search city online and retrieve latitude and longitude of searched city69 }
52 XmlListModel {70 }
53 id: searchCityModel;71 }
5472
55 source: localCityUrl()73 // Search Bar to search for cities (online)
56 query: "/geonames/geoname"74 // TODO: Add capability to also search cities provided locally with clock-app
5775 SearchBox {
58 XmlRole { name: "city"; query: "toponymName/string()"; isKey: true }76 id: searchBox
59 XmlRole { name: "country"; query: "countryName/string()"; isKey: true }77
60 XmlRole { name: "lat"; query: "lat/string()"; isKey: true }78 height: units.gu(4)
61 XmlRole { name: "lng"; query: "lng/string()"; isKey: true }79 anchors { left: parent.left; right: parent.right }
62 XmlRole { name: "adminName"; query: "adminName1/string()"; isKey: true}80
63 XmlRole { name: "adminName2"; query: "adminName2/string()"; isKey: true}81 onSearchTriggered: {
6482 if (search_string != "")
65 onSourceChanged: reload();83 searchCityModel.source = searchCityModel.searchCityUrl(search_string);
66 }84 else
6785 searchCityModel.source = searchCityModel.localCityUrl()
68 Item {86 }
69 id: searchBox;87 }
7088
71 anchors { left: parent.left; right: parent.right }89 // List to display the search results
72 height: units.gu(4)90 ListView {
7391 id: worldList
74 TextField {92 objectName: "worldList"
75 id: searchLabel93
7694 anchors { left: parent.left; right: parent.right }
77 anchors { left: parent.left; leftMargin: units.gu(2); top: parent.top; bottom: parent.bottom; right: searchButton.left; rightMargin: -units.gu(1) }95 height: units.gu(50) // TODO: Make list view dynamic rather than the fixed 50 units gu.
78 width: worldClocks.width/1.596 model: searchCityModel
79 hasClearButton: true97 currentIndex: -1
80 placeholderText: i18n.tr("Search")98 clip: true;
81 primaryItem: Image {99
82 height: parent.height/2100 Component {
83 width: parent.height/2101 id: sectionHeading
84 source: Qt.resolvedUrl("../images/search_icon.svg")102 ListItem.Header {
85 }103 Label {
86104 text: section
87 // Provide a small pause before going online to search105 anchors { verticalCenter: parent.verticalCenter; left: parent.left; leftMargin: units.gu(2) }
88 Timer {106 color: Theme.palette.normal.baseText
89 id: search_timer107 fontSize: "medium"
90108 }
91 interval: 1100109 }
92 repeat: false110 }
93 onTriggered: {111
94 if (searchLabel.text != "") {112 Label {
95 search_string = searchLabel.text;113 id: errorMessage
96 searchCityModel.source = searchCityUrl(search_string);114 width: parent.width
97 } else {115 fontSize: "large"
98 searchCityModel.source = localCityUrl()116 wrapMode: Text.WordWrap
99 }117 horizontalAlignment: TextInput.AlignHCenter
100 }118 anchors.horizontalCenter: parent.horizontalCenter
101 }119 visible: worldList.count == 0 || searchCityModel.status == XmlListModel.Error || cityDetailsModel.status == XmlListModel.Error
102120 text: searchCityModel.status == XmlListModel.Error || cityDetailsModel.status == XmlListModel.Error ? i18n.tr("Unable to search online \nPlease check your internet connection") : i18n.tr("No results found")
103 onTextChanged: search_timer.restart()121 }
104122
105 InverseMouseArea {123 section.property: !errorMessage.visible ? "city" : ""
106 visible: searchLabel.activeFocus124 section.criteria: ViewSection.FirstCharacter
107 anchors.fill: parent125 section.delegate: sectionHeading;
108 onClicked: {126 section.labelPositioning: ViewSection.InlineLabels
109 worldClocks.forceActiveFocus()127
110 mouse.accepted = false128 delegate: ListItem.Base {
111 }129 visible: !errorMessage.visible
112 }130 height: adminName || adminName2 ? units.gu(8) : units.gu(6)
113 }131 Column {
114132 anchors { top: parent.top; topMargin: units.gu(0.5); left: parent.left; leftMargin: units.gu(3) }
115 Item {133 Label {
116 id: searchButton;134 id: cityDelegate
117 anchors { right: parent.right; rightMargin: units.gu(6.5); top: parent.top; bottom: parent.bottom }135 text: city
118 height: searchLabel.height136 color: Theme.palette.normal.baseText
119 visible: !searchActivity.running137 fontSize: "large"
120 Image {138 }
121 id: name139 Label {
122 source: Qt.resolvedUrl("../images/search_item.svg")140 id: stateDelegate
123 }141 width: worldClocks.width
124142 elide: Text.ElideRight
125 // Make searchButton clickable143 text: {
126 MouseArea {144 if (adminName && adminName2)
127 id: searchArea;145 return adminName2 + ", " + adminName
128 enabled: true;146 else if(adminName)
129 anchors.fill: searchButton147 return adminName
130 onClicked: {148 else if(adminName2)
131 if (searchLabel.text != "") {149 return adminName2
132 search_string = searchLabel.text;150 else
133 searchCityModel.source = searchCityUrl(search_string);151 return ""
134 }152 }
135 }153 visible: text == "" ? false : true
136 }154 color: Theme.palette.normal.baseText
137 }155 fontSize: "small"
138156 }
139157 Label {
140 ActivityIndicator {158 id: countryDelegate
141 id: searchActivity159 text: country
142 anchors { verticalCenter: searchButton.verticalCenter; left: searchButton.left; leftMargin: units.gu(1.5) }160 color: Theme.palette.normal.baseText
143 running: searchCityModel.status === XmlListModel.Loading161 fontSize: "small"
144162 }
145 }163 }
146 }164
147165 selected: worldList.currentIndex == index;
148 ListView {166
149 id: worldList167 onClicked: {
150 objectName: "worldList"168 worldList.currentIndex = index;
151169 worldClocks.lat = searchCityModel.get(index).lat;
152 anchors { left: parent.left; right: parent.right }170 worldClocks.lng = searchCityModel.get(index).lng;
153 height: units.gu(50)171 worldClocks.city = searchCityModel.get(index).city;
154 model: searchCityModel172 cityDetailsModel.source = cityDetailsModel.getCityTimezoneUrl(worldClocks.lat, worldClocks.lng)
155 currentIndex: -1173 }
156 clip: true;174 }
157175
158 Component {176 Scrollbar {
159 id: sectionHeading177 flickableItem: worldList;
160 ListItem.Header {178 align: Qt.AlignTrailing;
161 Label {
162 text: section
163 anchors { verticalCenter: parent.verticalCenter; left: parent.left; leftMargin: units.gu(2) }
164 color: Theme.palette.normal.baseText
165 fontSize: "medium"
166 }
167 }
168 }
169
170 Label {
171 id: errorMessage
172 width: parent.width
173 fontSize: "large"
174 wrapMode: Text.WordWrap
175 horizontalAlignment: TextInput.AlignHCenter
176 anchors.horizontalCenter: parent.horizontalCenter
177 visible: worldList.count == 0 || searchCityModel.status == XmlListModel.Error || cityDetailsModel.status == XmlListModel.Error
178 text: searchCityModel.status == XmlListModel.Error || cityDetailsModel.status == XmlListModel.Error ? i18n.tr("Unable to search online \nPlease check your internet connection") : i18n.tr("No results found")
179 }
180
181 section.property: !errorMessage.visible ? "city" : ""
182 section.criteria: ViewSection.FirstCharacter
183 section.delegate: sectionHeading;
184 section.labelPositioning: ViewSection.InlineLabels
185
186 delegate: ListItem.Base {
187 visible: !errorMessage.visible
188 height: adminName || adminName2 ? units.gu(8) : units.gu(6)
189 Column {
190 anchors { top: parent.top; topMargin: units.gu(0.5); left: parent.left; leftMargin: units.gu(3) }
191 Label {
192 id: cityDelegate
193 text: city
194 color: Theme.palette.normal.baseText
195 fontSize: "large"
196 }
197 Label {
198 id: stateDelegate
199 width: worldClocks.width
200 elide: Text.ElideRight
201 text: {
202 if (adminName && adminName2)
203 return adminName2 + ", " + adminName
204 else if(adminName)
205 return adminName
206 else if(adminName2)
207 return adminName2
208 else
209 return ""
210 }
211 visible: text == "" ? false : true
212 color: Theme.palette.normal.baseText
213 fontSize: "small"
214 }
215 Label {
216 id: countryDelegate
217 text: country
218 color: Theme.palette.normal.baseText
219 fontSize: "small"
220 }
221 }
222
223 selected: worldList.currentIndex == index;
224
225 onClicked: {
226 worldList.currentIndex = index;
227 worldClocks.clicked()
228 }179 }
229 }180 }
230 }181 }
231182
=== removed file 'clock/WorldPage.qml'
--- clock/WorldPage.qml 2013-09-27 08:38:15 +0000
+++ clock/WorldPage.qml 1970-01-01 00:00:00 +0000
@@ -1,76 +0,0 @@
1/*
2 * Copyright (C) 2013 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Nekhelesh Ramananthan <krnekhelesh@gmail.com>
17 */
18
19import QtQuick 2.0
20import Ubuntu.Components 0.1
21import QtQuick.XmlListModel 2.0
22
23Page {
24 id: worldPage
25
26 property bool isWorldCity;
27
28 visible: false;
29 title: isWorldCity ? i18n.tr("Add City") : i18n.tr("Edit Current Location")
30
31 function getTimeDifference(data) {
32 var worldTime = data.split(' ')[1].split(':');
33 var hoursWT = parseInt(worldTime[0]);
34 var minutesWT = parseInt(worldTime[1]);
35 var now = new Date();
36 var UTCdiff = new Date().getTimezoneOffset()
37 now.setMinutes(now.getMinutes() + UTCdiff)
38
39 // Calculating world city time diff w.r.t UTC
40 return ((hoursWT - now.getHours()) * 60 + (minutesWT - now.getMinutes()));
41 }
42
43 WorldClock {
44 id: worldClockList;
45
46 anchors { fill: parent; topMargin: units.gu(2) }
47
48 // Xml model to retrieve timezone of searched city
49 XmlListModel {
50 id: cityDetailsModel;
51
52 source: worldClockList.getCityTimezoneUrl(worldClockList.lat, worldClockList.lng)
53 query: "/geonames/timezone"
54
55 onStatusChanged: {
56 if(status == XmlListModel.Ready && worldClockList.city != "null") {
57 if (isWorldCity)
58 worldModel.appendPreset(worldClockList.city, getTimeDifference(cityDetailsModel.get(0).time), worldClockList.lng, worldClockList.lat);
59 else
60 worldModel.appendCurrentLocation(worldClockList.city, worldClockList.lng, worldClockList.lat);
61 worldClockList.clearUserSearch();
62 pageStack.pop()
63 }
64 }
65
66 XmlRole { name: "time"; query: "time/string()" }
67 }
68
69 onClicked: {
70 lat = searchModel.get(index).lat;
71 lng = searchModel.get(index).lng;
72 city = searchModel.get(index).city;
73 cityDetailsModel.reload()
74 }
75 }
76}
770
=== modified file 'tests/autopilot/ubuntu_clock_app/emulators.py'
--- tests/autopilot/ubuntu_clock_app/emulators.py 2013-10-21 21:16:41 +0000
+++ tests/autopilot/ubuntu_clock_app/emulators.py 2013-10-22 17:11:13 +0000
@@ -294,7 +294,7 @@
294294
295 def get_world_cities_page(self):295 def get_world_cities_page(self):
296 """Return the page containing the world cities."""296 """Return the page containing the world cities."""
297 return self.select_single("WorldPage")297 return self.select_single("WorldClock")
298298
299 def get_clock_page(self):299 def get_clock_page(self):
300 """Return the page containing the main clock."""300 """Return the page containing the main clock."""

Subscribers

People subscribed via source and target branches