Merge lp:~nik90/ubuntu-clock-app/world-clock-part1 into lp:ubuntu-clock-app/saucy
- world-clock-part1
- Merge into saucy
Proposed by
Nekhelesh Ramananthan
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Nekhelesh Ramananthan | ||||
Approved revision: | 108 | ||||
Merged at revision: | 103 | ||||
Proposed branch: | lp:~nik90/ubuntu-clock-app/world-clock-part1 | ||||
Merge into: | lp:ubuntu-clock-app/saucy | ||||
Prerequisite: | lp:~nik90/ubuntu-clock-app/initial-visual-design | ||||
Diff against target: |
607 lines (+506/-59) 4 files modified
clock/ClockPage.qml (+220/-58) clock/EasterEgg.qml (+1/-1) clock/WorldClock.qml (+107/-0) clock/WorldClockModel.qml (+178/-0) |
||||
To merge this branch: | bzr merge lp:~nik90/ubuntu-clock-app/world-clock-part1 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu Phone Apps Jenkins Bot | continuous-integration | Approve | |
Nekhelesh Ramananthan | Pending | ||
Review via email: mp+168537@code.launchpad.net |
Commit message
Adds the first phase of the world clock's feature. It currently implements the following,
- Necessary UI to show the world clocks
- Search world clocks online using geoname.org API
- World Time is updated every minute locally using existing timer
- Add world clock local storage. Stores world clocks selected by the user
- Clicking on world clock sets it as the current location.
- Add Clock toolbar action
Description of the change
This MP achieves the following,
- Necessary UI to show the world clocks
- Search world clocks online using geoname.org API
- World Time is updated every minute locally using existing timer
- Add world clock local storage. Stores world clocks selected by the user
- Clicking on world clock sets it as the current location.
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:
Approve
(continuous-integration)
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'clock/ClockPage.qml' | |||
2 | --- clock/ClockPage.qml 2013-06-09 10:23:09 +0000 | |||
3 | +++ clock/ClockPage.qml 2013-06-14 19:10:32 +0000 | |||
4 | @@ -20,75 +20,237 @@ | |||
5 | 20 | 20 | ||
6 | 21 | import QtQuick 2.0 | 21 | import QtQuick 2.0 |
7 | 22 | import Ubuntu.Components 0.1 | 22 | import Ubuntu.Components 0.1 |
8 | 23 | import Ubuntu.Components.ListItems 0.1 as ListItem | ||
9 | 24 | import QtQuick.XmlListModel 2.0 | ||
10 | 23 | import "../common/ClockUtils.js" as Utils | 25 | import "../common/ClockUtils.js" as Utils |
11 | 24 | import "../common/Constants.js" as Constants | 26 | import "../common/Constants.js" as Constants |
14 | 25 | 27 | import "../common" | |
15 | 26 | Page { | 28 | |
16 | 29 | Page { | ||
17 | 30 | id: clockPage; | ||
18 | 31 | |||
19 | 27 | // Property to hold the formatted time string to show on the screen | 32 | // Property to hold the formatted time string to show on the screen |
20 | 28 | property string currentTimeFormatted | 33 | property string currentTimeFormatted |
21 | 34 | property real currentTimeStamp | ||
22 | 35 | property real diff: new Date().getTimezoneOffset() | ||
23 | 29 | 36 | ||
24 | 30 | Component.onCompleted: { | 37 | Component.onCompleted: { |
25 | 31 | Utils.log("ClockPage loaded"); | 38 | Utils.log("ClockPage loaded"); |
27 | 32 | currentTimeFormatted = Qt.formatTime(new Date()); | 39 | updateTime(); |
28 | 33 | } | 40 | } |
29 | 34 | 41 | ||
30 | 35 | function onTimerUpdate(now) { | 42 | function onTimerUpdate(now) { |
32 | 36 | currentTimeFormatted = Qt.formatTime(now); | 43 | currentTimeFormatted = Qt.formatTime(new Date(currentTimeStamp + (diff * 60000)), "hh:mm") |
33 | 44 | now.setMinutes(now.getMinutes() + diff) | ||
34 | 37 | clockFace.timeChanged(now) | 45 | clockFace.timeChanged(now) |
71 | 38 | } | 46 | if (now.getUTCSeconds() == 0) { |
72 | 39 | 47 | updateTime(); | |
73 | 40 | // Label to show the current time | 48 | } |
74 | 41 | Label { | 49 | } |
75 | 42 | id: currentTimeLabel | 50 | |
76 | 43 | objectName: "currentTimeLabel" | 51 | function updateTime() { |
77 | 44 | 52 | var now = new Date(); | |
78 | 45 | z: clockFace.z + 1; | 53 | currentTimeStamp = now.getTime(); |
79 | 46 | width: parent.width; height: parent.height / 4 | 54 | } |
80 | 47 | anchors.centerIn: clockFace | 55 | |
81 | 48 | horizontalAlignment: Text.AlignHCenter | 56 | function getTimeDifference(data) { |
82 | 49 | verticalAlignment: Text.AlignVCenter | 57 | var worldTime = data.split(' ')[1].split(':'), |
83 | 50 | fontSize: "x-large" | 58 | hoursWT = parseInt(worldTime[0]), |
84 | 51 | color: Constants.brightWhite; | 59 | minutesWT = parseInt(worldTime[1]), |
85 | 52 | 60 | now = new Date(); | |
86 | 53 | text: currentTimeFormatted | 61 | return ((hoursWT - now.getHours()) * 60 + (minutesWT - now.getMinutes())); |
87 | 54 | } | 62 | } |
88 | 55 | 63 | ||
89 | 56 | // Qml Element to draw the analogue clock face along with its hour, minute and second hands. | 64 | WorldClockModel { |
90 | 57 | AnalogClockFace { | 65 | id: worldModel; |
91 | 58 | id: clockFace | 66 | |
92 | 59 | 67 | Component.onCompleted: { | |
93 | 60 | anchors.centerIn: parent | 68 | _loadDB(); |
94 | 61 | Component.onCompleted: easterEggCircle.reloadSunRiseModel() | 69 | diff = worldModel.timeDiff; |
95 | 62 | 70 | currentLocation.text = worldModel.city; | |
96 | 63 | onClicked: { | 71 | currentTimeFormatted = Qt.formatTime(new Date(currentTimeStamp + (diff * 60000)), "hh:mm") |
97 | 64 | if (easterEggCircle.isReady == 1) { | 72 | } |
98 | 65 | clockFace.state == "" ? clockFace.state = "easteregg" : clockFace.state = ""; | 73 | } |
99 | 66 | } else { | 74 | |
100 | 67 | Utils.log("Sunrise/Sunset times not yet loaded.") | 75 | AnimationContainer { |
101 | 68 | } | 76 | id: clockAnimationContainer |
102 | 69 | } | 77 | |
103 | 70 | 78 | initYPos: units.gu(0); | |
104 | 71 | EasterEgg { | 79 | finalYPos: -listWorldClocks.height; |
105 | 72 | id: easterEggCircle | 80 | |
106 | 73 | anchors.centerIn: parent | 81 | anchors.fill: parent |
107 | 82 | |||
108 | 83 | // Label to show the current time | ||
109 | 84 | Rectangle { | ||
110 | 85 | id: labelContainer; | ||
111 | 86 | |||
112 | 87 | z: clockFace.z + 1; | ||
113 | 74 | width: clockFace.innerDimension; height: width; | 88 | width: clockFace.innerDimension; height: width; |
132 | 75 | radius: width / 2; | 89 | anchors.centerIn: clockFace; |
133 | 76 | } | 90 | color: "transparent" |
134 | 77 | 91 | ||
135 | 78 | states: [ | 92 | Label { |
136 | 79 | State { | 93 | id: currentTimeLabel |
137 | 80 | name: "easteregg" | 94 | objectName: "currentTimeLabel" |
138 | 81 | PropertyChanges { target: easterEggCircle; opacity: 1; } | 95 | |
139 | 82 | PropertyChanges { target: currentTimeLabel; opacity: 0; } | 96 | anchors.centerIn: parent |
140 | 83 | // TODO: Do some caching of data here. Store data locally to reduce API calls. | 97 | horizontalAlignment: Text.AlignHCenter |
141 | 84 | PropertyChanges { target: easterEggCircle; sunriseLabel: easterEggCircle.getSunTime(easterEggCircle.model.get(0).sunrise); } | 98 | verticalAlignment: Text.AlignVCenter |
142 | 85 | PropertyChanges { target: easterEggCircle; sunsetLabel: easterEggCircle.getSunTime(easterEggCircle.model.get(0).sunset); } | 99 | fontSize: "x-large" |
143 | 86 | }, | 100 | color: Constants.brightWhite; |
144 | 87 | State { | 101 | |
145 | 88 | name: "" | 102 | text: currentTimeFormatted |
146 | 89 | PropertyChanges { target: currentTimeLabel; opacity: 1; } | 103 | } |
147 | 90 | PropertyChanges { target: easterEggCircle; opacity: 0; } | 104 | } |
148 | 91 | } | 105 | |
149 | 92 | ] | 106 | // Qml Element to draw the analogue clock face along with its hour, minute and second hands. |
150 | 107 | AnalogClockFace { | ||
151 | 108 | id: clockFace | ||
152 | 109 | |||
153 | 110 | anchors { top: parent.top; topMargin: units.gu(17); horizontalCenter: parent.horizontalCenter } | ||
154 | 111 | Component.onCompleted: easterEggCircle.reloadSunRiseModel() | ||
155 | 112 | |||
156 | 113 | onClicked: { | ||
157 | 114 | if (easterEggCircle.isReady == XmlListModel.Ready) { | ||
158 | 115 | clockFace.state == "" ? clockFace.state = "easteregg" : clockFace.state = ""; | ||
159 | 116 | } else { | ||
160 | 117 | Utils.log("Sunrise/Sunset times not yet loaded.") | ||
161 | 118 | } | ||
162 | 119 | } | ||
163 | 120 | |||
164 | 121 | EasterEgg { | ||
165 | 122 | id: easterEggCircle | ||
166 | 123 | anchors.centerIn: parent | ||
167 | 124 | width: clockFace.innerDimension; height: width; | ||
168 | 125 | radius: width / 2; | ||
169 | 126 | } | ||
170 | 127 | |||
171 | 128 | states: [ | ||
172 | 129 | State { | ||
173 | 130 | name: "easteregg" | ||
174 | 131 | PropertyChanges { target: easterEggCircle; opacity: 1; } | ||
175 | 132 | PropertyChanges { target: currentTimeLabel; opacity: 0; } | ||
176 | 133 | // TODO: Do some caching of data here. Store data locally to reduce API calls. | ||
177 | 134 | PropertyChanges { target: easterEggCircle; sunriseLabel: easterEggCircle.getSunTime(easterEggCircle.model.get(0).sunrise); } | ||
178 | 135 | PropertyChanges { target: easterEggCircle; sunsetLabel: easterEggCircle.getSunTime(easterEggCircle.model.get(0).sunset); } | ||
179 | 136 | }, | ||
180 | 137 | State { | ||
181 | 138 | name: "" | ||
182 | 139 | PropertyChanges { target: currentTimeLabel; opacity: 1; } | ||
183 | 140 | PropertyChanges { target: easterEggCircle; opacity: 0; } | ||
184 | 141 | } | ||
185 | 142 | ] | ||
186 | 143 | } | ||
187 | 144 | |||
188 | 145 | Column { | ||
189 | 146 | id: savedWorldClock | ||
190 | 147 | |||
191 | 148 | height: childrenRect.height; | ||
192 | 149 | anchors { left:parent.left; right:parent.right; top: clockFace.bottom; topMargin: units.gu(12)} | ||
193 | 150 | |||
194 | 151 | ListItem.ThinDivider {} | ||
195 | 152 | |||
196 | 153 | ListItem.Header { text: i18n.tr("Current Location") } | ||
197 | 154 | |||
198 | 155 | ListItem.SingleValue { | ||
199 | 156 | id: currentLocation | ||
200 | 157 | text: "UTC" | ||
201 | 158 | value: Qt.formatTime(new Date(currentTimeStamp + (diff * 60000)), "hh:mm A"); | ||
202 | 159 | } | ||
203 | 160 | |||
204 | 161 | ListItem.Header { text: i18n.tr("World") } | ||
205 | 162 | |||
206 | 163 | ListView { | ||
207 | 164 | id: listWorldClocks | ||
208 | 165 | |||
209 | 166 | anchors { left: parent.left; right: parent.right } | ||
210 | 167 | height: units.gu(30) | ||
211 | 168 | model: worldModel | ||
212 | 169 | currentIndex: -1 | ||
213 | 170 | |||
214 | 171 | delegate: ListItem.Standard { | ||
215 | 172 | text: cityName; | ||
216 | 173 | |||
217 | 174 | Label { | ||
218 | 175 | fontSize: "medium" | ||
219 | 176 | text: Qt.formatTime(new Date(currentTimeStamp + (rawOffSet * 60000)), "hh:mm A"); | ||
220 | 177 | anchors { verticalCenter: parent.verticalCenter; right: parent.right; rightMargin: units.gu(2) } | ||
221 | 178 | } | ||
222 | 179 | |||
223 | 180 | selected: listWorldClocks.currentIndex == index | ||
224 | 181 | |||
225 | 182 | onClicked: { | ||
226 | 183 | currentLocation.text = cityName; | ||
227 | 184 | diff = rawOffSet; | ||
228 | 185 | worldModel.appendCurrentLocation(cityName, diff); | ||
229 | 186 | } | ||
230 | 187 | } | ||
231 | 188 | } | ||
232 | 189 | } | ||
233 | 190 | } | ||
234 | 191 | |||
235 | 192 | WorldClock { | ||
236 | 193 | id: worldClockList; | ||
237 | 194 | |||
238 | 195 | anchors { left: parent.left; right: parent.right; top: parent.top; topMargin: units.gu(2) } | ||
239 | 196 | |||
240 | 197 | // Xml model to retrieve timezone of searched city | ||
241 | 198 | XmlListModel { | ||
242 | 199 | id: cityDetailsModel; | ||
243 | 200 | |||
244 | 201 | source: worldClockList.getCityTimezoneUrl(worldClockList.lat, worldClockList.lng) | ||
245 | 202 | query: "/geonames/timezone" | ||
246 | 203 | |||
247 | 204 | onStatusChanged: { | ||
248 | 205 | if(status == XmlListModel.Ready && worldClockList.city != "null") { | ||
249 | 206 | worldModel.appendPreset(worldClockList.city, getTimeDifference(cityDetailsModel.get(0).time)); | ||
250 | 207 | } | ||
251 | 208 | } | ||
252 | 209 | |||
253 | 210 | XmlRole { name: "time"; query: "time/string()" } | ||
254 | 211 | } | ||
255 | 212 | |||
256 | 213 | onClicked: { | ||
257 | 214 | lat = searchModel.get(index).lat; | ||
258 | 215 | lng = searchModel.get(index).lng; | ||
259 | 216 | city = searchModel.get(index).city; | ||
260 | 217 | cityDetailsModel.reload() | ||
261 | 218 | clockPage.state = "NORMAL" | ||
262 | 219 | } | ||
263 | 220 | } | ||
264 | 221 | |||
265 | 222 | states: [ | ||
266 | 223 | State { | ||
267 | 224 | name: "ADDCITY" | ||
268 | 225 | PropertyChanges { target: worldClockList; visible: true } | ||
269 | 226 | PropertyChanges { target: clockAnimationContainer; visible: false } | ||
270 | 227 | PropertyChanges { target: addCity; visible: false } | ||
271 | 228 | }, | ||
272 | 229 | |||
273 | 230 | State { | ||
274 | 231 | name: "NORMAL" | ||
275 | 232 | PropertyChanges { target: worldClockList; visible: false } | ||
276 | 233 | PropertyChanges { target: clockAnimationContainer; visible: true } | ||
277 | 234 | PropertyChanges { target: addCity; visible: true } | ||
278 | 235 | } | ||
279 | 236 | ] | ||
280 | 237 | |||
281 | 238 | tools: ToolbarActions { | ||
282 | 239 | id: toolbarClock | ||
283 | 240 | |||
284 | 241 | Action { | ||
285 | 242 | id: addCity | ||
286 | 243 | |||
287 | 244 | iconSource: Qt.resolvedUrl("../images/add_icon.png") | ||
288 | 245 | text: i18n.tr("Add City") | ||
289 | 246 | visible: true; | ||
290 | 247 | |||
291 | 248 | onTriggered: clockPage.state = "ADDCITY"; | ||
292 | 249 | } | ||
293 | 250 | |||
294 | 251 | back { | ||
295 | 252 | visible: true; | ||
296 | 253 | onTriggered: clockPage.state = "NORMAL"; | ||
297 | 254 | } | ||
298 | 93 | } | 255 | } |
299 | 94 | } | 256 | } |
300 | 95 | 257 | ||
301 | === modified file 'clock/EasterEgg.qml' | |||
302 | --- clock/EasterEgg.qml 2013-06-09 10:23:09 +0000 | |||
303 | +++ clock/EasterEgg.qml 2013-06-14 19:10:32 +0000 | |||
304 | @@ -52,7 +52,7 @@ | |||
305 | 52 | function getUrlString (latitude, longitude) { | 52 | function getUrlString (latitude, longitude) { |
306 | 53 | var base_url,url; | 53 | var base_url,url; |
307 | 54 | base_url = "http://api.openweathermap.org/data/2.5/weather?"; | 54 | base_url = "http://api.openweathermap.org/data/2.5/weather?"; |
309 | 55 | url = base_url + "lat=" + latitude + "&lon=" + longitude + "&mode=xml"; | 55 | url = base_url + "lat=" + latitude + "&lon=" + longitude + "&mode=xml"; |
310 | 56 | return url; | 56 | return url; |
311 | 57 | } | 57 | } |
312 | 58 | 58 | ||
313 | 59 | 59 | ||
314 | === added file 'clock/WorldClock.qml' | |||
315 | --- clock/WorldClock.qml 1970-01-01 00:00:00 +0000 | |||
316 | +++ clock/WorldClock.qml 2013-06-14 19:10:32 +0000 | |||
317 | @@ -0,0 +1,107 @@ | |||
318 | 1 | import QtQuick 2.0 | ||
319 | 2 | import Ubuntu.Components 0.1 | ||
320 | 3 | import Ubuntu.Components.ListItems 0.1 as ListItem | ||
321 | 4 | import QtQuick.XmlListModel 2.0 | ||
322 | 5 | import "../common/ClockUtils.js" as Utils | ||
323 | 6 | import "../common/Constants.js" as Constants | ||
324 | 7 | |||
325 | 8 | Column { | ||
326 | 9 | id: worldClocks | ||
327 | 10 | |||
328 | 11 | // Properties to store the user searched city details | ||
329 | 12 | property string city: "null"; | ||
330 | 13 | property real lng: 0; | ||
331 | 14 | property real lat: 0; | ||
332 | 15 | property real rawOffSet: 0; | ||
333 | 16 | |||
334 | 17 | // Property to store the city searched by the user | ||
335 | 18 | property string search_string: "london"; | ||
336 | 19 | |||
337 | 20 | // Properties to store the information related to api.geonames.org | ||
338 | 21 | readonly property string username: "krnekhelesh"; | ||
339 | 22 | readonly property string search_base_url: "http://api.geonames.org/search?q="; | ||
340 | 23 | readonly property string timezone_base_url: "http://api.geonames.org/timezone?lat="; | ||
341 | 24 | readonly property int no_of_results: 5; | ||
342 | 25 | |||
343 | 26 | property alias searchModel: searchCityModel; | ||
344 | 27 | property alias index: worldList.currentIndex; | ||
345 | 28 | |||
346 | 29 | signal clicked() | ||
347 | 30 | |||
348 | 31 | function searchCityUrl (city) { | ||
349 | 32 | return (search_base_url + search_string + "&maxRows="+ no_of_results + "&username=" + username); | ||
350 | 33 | } | ||
351 | 34 | |||
352 | 35 | function getCityTimezoneUrl (lat, lng) { | ||
353 | 36 | return (timezone_base_url + lat + "&lng=" + lng + "&username=" + username); | ||
354 | 37 | } | ||
355 | 38 | |||
356 | 39 | height: childrenRect.height; | ||
357 | 40 | visible: false; | ||
358 | 41 | |||
359 | 42 | // Xml model to search city online and retrieve latitude and longitude of searched city | ||
360 | 43 | XmlListModel { | ||
361 | 44 | id: searchCityModel; | ||
362 | 45 | |||
363 | 46 | source: searchCityUrl(city) | ||
364 | 47 | query: "/geonames/geoname" | ||
365 | 48 | |||
366 | 49 | XmlRole { name: "city"; query: "toponymName/string()"; isKey: true } | ||
367 | 50 | XmlRole { name: "lat"; query: "lat/string()"; isKey: true } | ||
368 | 51 | XmlRole { name: "lng"; query: "lng/string()"; isKey: true } | ||
369 | 52 | } | ||
370 | 53 | |||
371 | 54 | Row { | ||
372 | 55 | id: searchBox; | ||
373 | 56 | |||
374 | 57 | spacing: units.gu(1) | ||
375 | 58 | anchors.horizontalCenter: parent.horizontalCenter; | ||
376 | 59 | height: childrenRect.height; | ||
377 | 60 | |||
378 | 61 | TextField { | ||
379 | 62 | id: searchLabel | ||
380 | 63 | |||
381 | 64 | hasClearButton: true | ||
382 | 65 | placeholderText: i18n.tr("Search"); | ||
383 | 66 | } | ||
384 | 67 | |||
385 | 68 | Button { | ||
386 | 69 | id: searchButton; | ||
387 | 70 | |||
388 | 71 | width: units.gu(12); height: searchLabel.height; | ||
389 | 72 | color: Constants.green; | ||
390 | 73 | text: i18n.tr("Search") | ||
391 | 74 | |||
392 | 75 | onClicked: { | ||
393 | 76 | if (searchLabel.text != "") { | ||
394 | 77 | search_string = searchLabel.text; | ||
395 | 78 | searchCityModel.reload(); | ||
396 | 79 | } | ||
397 | 80 | } | ||
398 | 81 | } | ||
399 | 82 | |||
400 | 83 | ActivityIndicator { | ||
401 | 84 | running: searchCityModel.status === XmlListModel.Loading | ||
402 | 85 | } | ||
403 | 86 | } | ||
404 | 87 | |||
405 | 88 | ListView { | ||
406 | 89 | id: worldList | ||
407 | 90 | |||
408 | 91 | anchors { left: parent.left; right: parent.right } | ||
409 | 92 | height: units.gu(35) | ||
410 | 93 | model: searchCityModel | ||
411 | 94 | currentIndex: -1 | ||
412 | 95 | |||
413 | 96 | delegate: ListItem.Standard { | ||
414 | 97 | id: delegateTest; | ||
415 | 98 | text: searchCityModel.status == XmlListModel.Ready ? searchCityModel.get(index).city : i18n.tr("Loading..."); | ||
416 | 99 | selected: worldList.currentIndex == index; | ||
417 | 100 | |||
418 | 101 | onClicked: { | ||
419 | 102 | worldList.currentIndex = index; | ||
420 | 103 | worldClocks.clicked() | ||
421 | 104 | } | ||
422 | 105 | } | ||
423 | 106 | } | ||
424 | 107 | } | ||
425 | 0 | 108 | ||
426 | === added file 'clock/WorldClockModel.qml' | |||
427 | --- clock/WorldClockModel.qml 1970-01-01 00:00:00 +0000 | |||
428 | +++ clock/WorldClockModel.qml 2013-06-14 19:10:32 +0000 | |||
429 | @@ -0,0 +1,178 @@ | |||
430 | 1 | /* | ||
431 | 2 | * Copyright (C) 2013 Canonical Ltd | ||
432 | 3 | * | ||
433 | 4 | * This program is free software: you can redistribute it and/or modify | ||
434 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
435 | 6 | * published by the Free Software Foundation. | ||
436 | 7 | * | ||
437 | 8 | * This program is distributed in the hope that it will be useful, | ||
438 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
439 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
440 | 11 | * GNU General Public License for more details. | ||
441 | 12 | * | ||
442 | 13 | * You should have received a copy of the GNU General Public License | ||
443 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
444 | 15 | * | ||
445 | 16 | * Authored by: Nekhelesh Ramananthan <krnekhelesh@gmail.com> | ||
446 | 17 | */ | ||
447 | 18 | |||
448 | 19 | import QtQuick 2.0 | ||
449 | 20 | import QtQuick.LocalStorage 2.0 | ||
450 | 21 | import "../common/ClockUtils.js" as Utils | ||
451 | 22 | |||
452 | 23 | ListModel { | ||
453 | 24 | id: model | ||
454 | 25 | |||
455 | 26 | readonly property string dbName: "ubuntu-clock-app" | ||
456 | 27 | readonly property string dbVersion: "0.1" | ||
457 | 28 | readonly property string dbDescription: "World Clock" | ||
458 | 29 | |||
459 | 30 | property real timeDiff: new Date().getTimezoneOffset() | ||
460 | 31 | property string city: "UTC"; | ||
461 | 32 | |||
462 | 33 | property var _db: null | ||
463 | 34 | |||
464 | 35 | function appendCurrentLocation(cityname, rawOffSet) | ||
465 | 36 | { | ||
466 | 37 | try { | ||
467 | 38 | _db.transaction(function(tx){ | ||
468 | 39 | var currentValue; | ||
469 | 40 | var res = tx.executeSql('SELECT * FROM CurrentClock'); | ||
470 | 41 | if (res.rows.length > 0) { | ||
471 | 42 | currentValue = res.rows.item(0).rawOffSet; | ||
472 | 43 | } | ||
473 | 44 | |||
474 | 45 | if (currentValue !== rawOffSet) { | ||
475 | 46 | // Update existing value or insert if none | ||
476 | 47 | Utils.log("Outer loop"); | ||
477 | 48 | if (currentValue !== undefined) { | ||
478 | 49 | res = tx.executeSql('UPDATE CurrentClock SET rawOffSet = ?, cityName = ?', [rawOffSet, cityname]); | ||
479 | 50 | Utils.log("Existing record found. Hence updating it!"); | ||
480 | 51 | } else { | ||
481 | 52 | res = tx.executeSql('INSERT INTO CurrentClock VALUES(?, ?)', [cityname, rawOffSet]); | ||
482 | 53 | Utils.log("No City found. Creating a new entry."); | ||
483 | 54 | } | ||
484 | 55 | } | ||
485 | 56 | }); | ||
486 | 57 | } catch (err) { | ||
487 | 58 | Utils.error("Error setting current location '" + "': " + err); | ||
488 | 59 | return false; | ||
489 | 60 | } | ||
490 | 61 | } | ||
491 | 62 | |||
492 | 63 | function appendPreset(cityName, rawOffSet) | ||
493 | 64 | { | ||
494 | 65 | if (_appendDB(cityName, rawOffSet)) { | ||
495 | 66 | model.append({"cityName" : cityName, "rawOffSet" : rawOffSet}) | ||
496 | 67 | } | ||
497 | 68 | } | ||
498 | 69 | |||
499 | 70 | function removePreset(index) | ||
500 | 71 | { | ||
501 | 72 | var cityName = get(index).cityName | ||
502 | 73 | if (_removeDB(cityName)) { | ||
503 | 74 | model.remove(index); | ||
504 | 75 | } | ||
505 | 76 | } | ||
506 | 77 | |||
507 | 78 | function _createDB() { | ||
508 | 79 | if (_db == null) return false; | ||
509 | 80 | try { | ||
510 | 81 | _db.transaction(function(tx){ | ||
511 | 82 | tx.executeSql('CREATE TABLE IF NOT EXISTS WorldClock(cityName TEXT, rawOffSet REAL)'); | ||
512 | 83 | }); | ||
513 | 84 | } catch (err) { | ||
514 | 85 | Utils.error("Error creating WorldClock table in db:" + err); | ||
515 | 86 | return false; | ||
516 | 87 | } | ||
517 | 88 | try { | ||
518 | 89 | _db.transaction(function(tx){ | ||
519 | 90 | tx.executeSql('CREATE TABLE IF NOT EXISTS CurrentClock(cityName TEXT, rawOffSet REAL)'); | ||
520 | 91 | }); | ||
521 | 92 | } catch (err) { | ||
522 | 93 | Utils.error("Error creating CurrentClock table in db:" + err); | ||
523 | 94 | return false; | ||
524 | 95 | } | ||
525 | 96 | return true; | ||
526 | 97 | } | ||
527 | 98 | |||
528 | 99 | function _loadDB() { | ||
529 | 100 | _db = LocalStorage.openDatabaseSync(model.dbName, model.dbVersion, model.dbDescription, 1000); | ||
530 | 101 | if (_db == null) return false; | ||
531 | 102 | if (!_createDB()) { | ||
532 | 103 | _db = null; | ||
533 | 104 | return false; | ||
534 | 105 | } | ||
535 | 106 | try { | ||
536 | 107 | _db.readTransaction(function(tx){ | ||
537 | 108 | var res = tx.executeSql('SELECT * FROM WorldClock'); | ||
538 | 109 | if (res.rows.length > 0) { | ||
539 | 110 | for (var i = 0; i < res.rows.length; i++) { | ||
540 | 111 | model.append({"cityName" : res.rows.item(i).cityName, | ||
541 | 112 | "rawOffSet" : res.rows.item(i).rawOffSet}); | ||
542 | 113 | } | ||
543 | 114 | } | ||
544 | 115 | }); | ||
545 | 116 | } catch (err) { | ||
546 | 117 | Utils.error("Error opening database: " + err); | ||
547 | 118 | _db = null | ||
548 | 119 | return false; | ||
549 | 120 | } | ||
550 | 121 | try { | ||
551 | 122 | _db.readTransaction(function(tx){ | ||
552 | 123 | var currentstate = tx.executeSql('SELECT * FROM CurrentClock ORDER BY cityName'); | ||
553 | 124 | if (currentstate.rows.length > 0) { | ||
554 | 125 | city = currentstate.rows.item(0).cityName; | ||
555 | 126 | timeDiff = currentstate.rows.item(0).rawOffSet | ||
556 | 127 | Utils.log("Inside reading") | ||
557 | 128 | } | ||
558 | 129 | }); | ||
559 | 130 | } catch (err) { | ||
560 | 131 | Utils.error("Error opening database: " + err); | ||
561 | 132 | _db = null | ||
562 | 133 | return false; | ||
563 | 134 | } | ||
564 | 135 | return true; | ||
565 | 136 | } | ||
566 | 137 | |||
567 | 138 | function _appendDB(cityName, rawOffSet) { | ||
568 | 139 | if (_db == null) return false; | ||
569 | 140 | |||
570 | 141 | try { | ||
571 | 142 | _db.transaction(function(tx){ | ||
572 | 143 | var currentValue; | ||
573 | 144 | var res = tx.executeSql('SELECT rawOffSet FROM WorldClock WHERE cityName=?', cityName); | ||
574 | 145 | if (res.rows.length > 0) { | ||
575 | 146 | currentValue = res.rows.item(0).rawOffSet; | ||
576 | 147 | } | ||
577 | 148 | |||
578 | 149 | if (currentValue !== rawOffSet) { | ||
579 | 150 | // Update existing value or insert if none | ||
580 | 151 | if (currentValue !== undefined) { | ||
581 | 152 | res = tx.executeSql('UPDATE WorldClock SET rawOffSet = ? WHERE cityName = ?', [rawOffSet, cityName]); | ||
582 | 153 | } else { | ||
583 | 154 | res = tx.executeSql('INSERT INTO WorldClock VALUES(?, ?)', [cityName, rawOffSet]); | ||
584 | 155 | } | ||
585 | 156 | } | ||
586 | 157 | }); | ||
587 | 158 | } catch (err) { | ||
588 | 159 | Utils.error("Error setting labeltxt '"+ label + "': " + err); | ||
589 | 160 | return false; | ||
590 | 161 | } | ||
591 | 162 | return true; | ||
592 | 163 | } | ||
593 | 164 | |||
594 | 165 | function _removeDB(cityName) { | ||
595 | 166 | if (_db == null) return false; | ||
596 | 167 | |||
597 | 168 | try { | ||
598 | 169 | _db.transaction(function(tx){ | ||
599 | 170 | tx.executeSql('DELETE FROM WorldClock WHERE cityName = ?', [cityName]); | ||
600 | 171 | }); | ||
601 | 172 | } catch (err) { | ||
602 | 173 | Utils.error("Error delete WorldClock'" + cityName + "': " + err); | ||
603 | 174 | return false; | ||
604 | 175 | } | ||
605 | 176 | return true; | ||
606 | 177 | } | ||
607 | 178 | } |
PASSED: Continuous integration, rev:108 91.189. 93.125: 8080/job/ ubuntu- clock-app- ci/73/ 91.189. 93.125: 8080/job/ ubuntu- clock-app- quantal- amd64-ci/ 38 91.189. 93.125: 8080/job/ ubuntu- clock-app- raring- amd64-ci/ 73
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild: 91.189. 93.125: 8080/job/ ubuntu- clock-app- ci/73/rebuild
http://