|
1 <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
|
2 <html lang="en" xml:lang="en"> |
|
3 <head> |
|
4 <meta content="text/html; charset=utf-8" http-equiv="Content-Type" /> |
|
5 <meta name="copyright" content="(C) Copyright 2005" /> |
|
6 <meta name="DC.rights.owner" content="(C) Copyright 2005" /> |
|
7 <meta content="concept" name="DC.Type" /> |
|
8 <meta name="DC.Title" content="Functionality" /> |
|
9 <meta scheme="URI" name="DC.Relation" content="WRTKit_Travel_Companion_Tutorial-GUID-be79ba64-fa03-4968-964e-d7dcc42d7053.html" /> |
|
10 <meta content="XHTML" name="DC.Format" /> |
|
11 <meta content="GUID-384F2430-3DE9-4006-AC8E-86C5774C3A79" name="DC.Identifier" /> |
|
12 <meta content="en" name="DC.Language" /> |
|
13 <link href="commonltr.css" type="text/css" rel="stylesheet" /> |
|
14 <title> |
|
15 Functionality</title> |
|
16 </head> |
|
17 <body id="GUID-384F2430-3DE9-4006-AC8E-86C5774C3A79"><a name="GUID-384F2430-3DE9-4006-AC8E-86C5774C3A79"><!-- --></a> |
|
18 |
|
19 |
|
20 |
|
21 <h1 class="topictitle1"> |
|
22 Functionality</h1> |
|
23 |
|
24 <div> |
|
25 |
|
26 <div class="section"><h2 class="sectiontitle"> |
|
27 Weather forecast</h2> |
|
28 |
|
29 |
|
30 <p> |
|
31 |
|
32 Let's continue by implementing the weather forecast view. This view will |
|
33 be entirely composed of non-foldable ContentPanel controls. We'll have one |
|
34 panel that displays the city for which the forecast is for and one panel |
|
35 for each day of the five-day forecast. In order to make it easy to change |
|
36 the number of days the forecast is for we'll store references to the forecast |
|
37 panels in an array. Thus, we need to add two global variables to the widget: |
|
38 </p> |
|
39 |
|
40 <pre> |
|
41 |
|
42 // Weather view controls. |
|
43 var weatherCityPanel; |
|
44 var weatherContentPanels; |
|
45 </pre> |
|
46 |
|
47 <p> |
|
48 |
|
49 The next step is to create the actual view in the createWeatherView() function. |
|
50 Of course the actual content for the content panels won't be known until at |
|
51 runtime when we know what city the forecast is for and what the actual weather |
|
52 forecast is. Still, we can create the actual content panels and just set their |
|
53 content later so this is not a problem. |
|
54 </p> |
|
55 |
|
56 <pre> |
|
57 |
|
58 // Creates the weather view. |
|
59 function createWeatherView() { |
|
60 // empty caption text to display the caption bar - custom background using CSS |
|
61 weatherView = new ListView(null, ""); |
|
62 |
|
63 // heading panel for city |
|
64 weatherCityPanel = new ContentPanel(); |
|
65 weatherView.addControl(weatherCityPanel); |
|
66 |
|
67 // create five content panels - one for each day in the 5-day forecast |
|
68 weatherContentPanels = []; |
|
69 for (var i = 0; i < 5; i++) { |
|
70 var weatherContentPanel = new ContentPanel(); |
|
71 weatherView.addControl(weatherContentPanel) |
|
72 weatherContentPanels.push(weatherContentPanel); |
|
73 } |
|
74 } |
|
75 </pre> |
|
76 |
|
77 <p> |
|
78 |
|
79 When we show the weather view we have to check what the current local city |
|
80 is and set the weatherCityPanel's content to a suitable HTML fragment that |
|
81 will work as a heading for the weather forecast. In addition to that we'll |
|
82 have to retrieve the weather forecast from the business logic engine and then |
|
83 create HTML from that for each day in the forecast and set it to the five |
|
84 content panels that we are storing references to in the weatherContentPanels |
|
85 array. The caption for the weather forecast panels will be a string that |
|
86 indicates the day that the forecast is for. |
|
87 </p> |
|
88 |
|
89 <p> |
|
90 |
|
91 Creating the HTML fragment for the forecast heading doesn't sound so hard |
|
92 but generating HTML for a weather forecast for five days sounds a bit tricky. |
|
93 Let's create a separate function to do that. Remember that the weather object |
|
94 that the engine is returning to us has two properties: temperature and type. |
|
95 The temperature is in the preferred temperature unit and the type is one of |
|
96 the type codes, e.g. "Sunny". We'll use it to match it up with a weather icon. |
|
97 </p> |
|
98 |
|
99 <pre> |
|
100 |
|
101 // Returns HTML for one day of weather forecast. |
|
102 function getHTMLForWeather(weather) { |
|
103 // build weather icon file name string |
|
104 var weatherIcon = "Weather" + weather.type + ".png"; |
|
105 |
|
106 // build temperature string |
|
107 var temperatureStr = weather.temperature + "&deg;" + engine.getTemperatureUnit().toUpperCase(); |
|
108 |
|
109 // build weather HTML |
|
110 var weatherBuf = ""; |
|
111 weatherBuf += "<table class=\"WeatherForecastDayTable\"><tr>"; |
|
112 weatherBuf += "<td class=\"WeatherForecastIconCell\">"; |
|
113 weatherBuf += "<img src=\"" + weatherIcon + "\"/>"; |
|
114 weatherBuf += "</td>"; |
|
115 weatherBuf += "<td class=\"WeatherForecastTemperatureCell\">"; |
|
116 weatherBuf += temperatureStr; |
|
117 weatherBuf += "</td>"; |
|
118 weatherBuf += "</tr></table>"; |
|
119 |
|
120 return weatherBuf; |
|
121 } |
|
122 </pre> |
|
123 |
|
124 <p> |
|
125 |
|
126 The HTML that we are generating is a table with a single row with two cells. |
|
127 The left cell has the weather icon image and the right cell has the temperature. |
|
128 We are using three CSS style rules for the table and we naturally need to create |
|
129 those too in the TravelCompanion.css file: |
|
130 </p> |
|
131 |
|
132 <pre> |
|
133 |
|
134 /* Table for one day of weather forecast information */ |
|
135 .WeatherForecastDayTable { |
|
136 margin: auto; |
|
137 border-spacing: 0px; |
|
138 } |
|
139 |
|
140 /* Table cell for weather icon */ |
|
141 .WeatherForecastIconCell { |
|
142 line-height: 1px; |
|
143 font-size: 1px; |
|
144 vertical-align: middle; |
|
145 } |
|
146 |
|
147 /* Table cell for temperature information */ |
|
148 .WeatherForecastTemperatureCell { |
|
149 padding: 0px 0px 0px 10px; |
|
150 vertical-align: middle; |
|
151 } |
|
152 </pre> |
|
153 |
|
154 <p> |
|
155 |
|
156 Now we have a way to turn a weather forecast into HTML that we can use in a |
|
157 ContentPanel control. We'll do that in a function we call updateWeatherForecast(). |
|
158 The caption for each weather forecast panel will indicate the day that the |
|
159 forecast is for. The first weather object in the array that the engine returns |
|
160 is for today and the subsequent ones are for coming days. |
|
161 </p> |
|
162 |
|
163 <pre> |
|
164 |
|
165 // Updates the weather forecast. |
|
166 function updateWeatherForecast() { |
|
167 // get local time and weather |
|
168 var localTime = engine.getLocalTime(); |
|
169 var localWeather = engine.getLocalWeather(); |
|
170 |
|
171 // set the weather for each day in the forecast |
|
172 for (var i = 0; i < 5; i++) { |
|
173 // figure out day name |
|
174 var day = (localTime.day + i) % 7; |
|
175 var dayName = localTime.dayNames[day]; |
|
176 |
|
177 // set weather to content panel |
|
178 weatherContentPanels[i].setCaption((i == 0 ? "Today, " : "") + dayName); |
|
179 weatherContentPanels[i].setContent(getHTMLForWeather(localWeather[i])); |
|
180 } |
|
181 } |
|
182 </pre> |
|
183 |
|
184 <p> |
|
185 |
|
186 We are now ready to implement the showWeatherView() function: |
|
187 </p> |
|
188 |
|
189 <pre> |
|
190 |
|
191 // Displays the weather view. |
|
192 function showWeatherView() { |
|
193 // set heading city panel |
|
194 weatherCityPanel.setContent("<div class=\"WeatherCityPanel\">" + engine.getLocalCity().name + " 5-day Forecast("</div>"); |
|
195 |
|
196 // update the weather forecast before showing the view |
|
197 updateWeatherForecast(); |
|
198 |
|
199 setSubViewSoftkeys(); |
|
200 uiManager.setView(weatherView); |
|
201 } |
|
202 </pre> |
|
203 |
|
204 <p> |
|
205 |
|
206 The weatherCityPanel gets a heading that matches the currently configured local city. |
|
207 We're using another CSS rule for that so we'll have to add it to our stylesheet: |
|
208 </p> |
|
209 |
|
210 <pre> |
|
211 |
|
212 /* City heading panel for weather forecast view */ |
|
213 .WeatherCityPanel { |
|
214 font-size: 14px; |
|
215 font-weight: bold; |
|
216 padding: 0px 0px 10px 0px; |
|
217 } |
|
218 </pre> |
|
219 |
|
220 <p> |
|
221 |
|
222 The weather forecast view and functionality is now ready to be tested. |
|
223 </p> |
|
224 |
|
225 </div> |
|
226 |
|
227 <div class="section"><h2 class="sectiontitle"> |
|
228 Currency converter</h2> |
|
229 |
|
230 |
|
231 <p> |
|
232 |
|
233 It's time to move on to the currency converter. This view is going to have two |
|
234 TextField controls. One for home currency and one for local currency. In addition |
|
235 there will be two FormButton controls to convert from the home currency to the |
|
236 local currency, and vice versa. We'll need references to the textfields so that |
|
237 we can retrieve their values when the form buttons are pressed: |
|
238 </p> |
|
239 |
|
240 <pre> |
|
241 |
|
242 // Converter view controls. |
|
243 var homeMoneyField; |
|
244 var localMoneyField; |
|
245 </pre> |
|
246 |
|
247 <p> |
|
248 |
|
249 Creating the view is quite straight forward: |
|
250 </p> |
|
251 |
|
252 <pre> |
|
253 |
|
254 // Creates the converter view. |
|
255 function createConverterView() { |
|
256 // empty caption text to display the caption bar - custom background using CSS |
|
257 converterView = new ListView(null, ""); |
|
258 |
|
259 // home money |
|
260 homeMoneyField = new TextField(); |
|
261 converterView.addControl(homeMoneyField); |
|
262 |
|
263 // local money |
|
264 localMoneyField = new TextField(); |
|
265 converterView.addControl(localMoneyField); |
|
266 |
|
267 // home to local |
|
268 var homeToLocalButton = new FormButton(null, "Convert Home to Local"); |
|
269 converterView.addControl(homeToLocalButton); |
|
270 |
|
271 // local to home |
|
272 var localToHomeButton = new FormButton(null, "Convert Local to Home"); |
|
273 converterView.addControl(localToHomeButton); |
|
274 } |
|
275 </pre> |
|
276 |
|
277 <p> |
|
278 |
|
279 Notice that the textfields don't have any caption or text at this point. We will |
|
280 set that just before the view is displayed because the caption will depend on |
|
281 what the home and local cities are configured to. |
|
282 </p> |
|
283 |
|
284 <p> |
|
285 |
|
286 Also notice that we don't have any event listeners for the buttons at this point. |
|
287 That's because we haven't actually written any functions that will implement |
|
288 the currency conversion yet. But before we do that we'll implement the |
|
289 function to show the view: |
|
290 </p> |
|
291 |
|
292 <pre> |
|
293 |
|
294 // Displays the converter view. |
|
295 function showConverterView() { |
|
296 // set captions and reset fields |
|
297 homeMoneyField.setCaption("Home Currency (" + engine.getHomeCity().currency + ")"); |
|
298 homeMoneyField.setText(""); |
|
299 localMoneyField.setCaption("Local Currency (" + engine.getLocalCity().currency + ")"); |
|
300 localMoneyField.setText(""); |
|
301 |
|
302 setSubViewSoftkeys(); |
|
303 uiManager.setView(converterView); |
|
304 } |
|
305 </pre> |
|
306 |
|
307 <p> |
|
308 |
|
309 You can now test the view but it won't actually convert any currency until |
|
310 we give our form buttons event listeners and write the code that will be |
|
311 called when the buttons are pressed. |
|
312 </p> |
|
313 |
|
314 <p> |
|
315 |
|
316 We'll implement that in two functions. One to convert from home to local and |
|
317 another to convert in the other direction. The functions take the current |
|
318 text value from the corresponding field and then pass it to the engine to do |
|
319 the conversion. The result is then placed in the other of the two fields, |
|
320 formatted so that the result has exactly two digits. |
|
321 </p> |
|
322 |
|
323 <pre> |
|
324 |
|
325 // Called when the user clicks on the "convert home to local" button |
|
326 // in the converter view. (rounds to two decimals) |
|
327 function convertHomeToLocalMoney() { |
|
328 var homeMoney = parseFloat(homeMoneyField.getText()); |
|
329 var localMoney = engine.convertHomeToLocalMoney(homeMoney).toFixed(2); |
|
330 localMoneyField.setText(localMoney); |
|
331 } |
|
332 |
|
333 // Called when the user clicks on the "convert local to home" button |
|
334 // in the converter view. (rounds to two decimals) |
|
335 function convertLocalToHomeMoney() { |
|
336 var localMoney = parseFloat(localMoneyField.getText()); |
|
337 var homeMoney = engine.convertLocalToHomeMoney(localMoney).toFixed(2); |
|
338 homeMoneyField.setText(homeMoney); |
|
339 } |
|
340 </pre> |
|
341 |
|
342 <p> |
|
343 |
|
344 Now that the functions are implemented we can add the event listeners to our |
|
345 form buttons. We'll add this code right after where the buttons are created in |
|
346 the createConverterView() function: |
|
347 </p> |
|
348 |
|
349 <pre> |
|
350 |
|
351 homeToLocalButton.addEventListener("ActionPerformed", convertHomeToLocalMoney); |
|
352 localToHomeButton.addEventListener("ActionPerformed", convertLocalToHomeMoney); |
|
353 </pre> |
|
354 |
|
355 <p> |
|
356 |
|
357 The currency converter is now working and it's time to test it. Try it with |
|
358 different currencies by changing the home and local cities in the settings view. |
|
359 </p> |
|
360 |
|
361 <div class="fignone" id="GUID-384F2430-3DE9-4006-AC8E-86C5774C3A79__GUID-D5A1648B-B81D-46B0-A15F-C3C8FF2185D7"><a name="GUID-384F2430-3DE9-4006-AC8E-86C5774C3A79__GUID-D5A1648B-B81D-46B0-A15F-C3C8FF2185D7"><!-- --></a><span class="figcap">Figure 1. |
|
362 Travel Companion converter view</span> |
|
363 |
|
364 |
|
365 <br /><img src="Travel_Companion_Converter_Screenshot_1.png" /><br /> |
|
366 </div> |
|
367 |
|
368 </div> |
|
369 |
|
370 <div class="section"><h2 class="sectiontitle"> |
|
371 Information view</h2> |
|
372 |
|
373 |
|
374 <p> |
|
375 |
|
376 The information view is a summary of relevant information that the user is |
|
377 will often need. Here we'll have a world clock that shows the home and local |
|
378 time, the current weather at home and in the local city, as well as the |
|
379 latest news headlines. We will use ContentPanel controls for all of these. |
|
380 One for the home time, one for the local time, one for the home weather, one |
|
381 for the local weather, and one for all the news headlines. |
|
382 </p> |
|
383 |
|
384 <p> |
|
385 |
|
386 We'll need to access all of these content panels from outside the view creation |
|
387 function so we'll need global variables to hold references to them: |
|
388 </p> |
|
389 |
|
390 <pre> |
|
391 |
|
392 // Info view controls. |
|
393 var homeCityTimePanel; |
|
394 var localCityTimePanel; |
|
395 var homeCityWeatherPanel; |
|
396 var localCityWeatherPanel; |
|
397 var newsHeadlinesPanel; |
|
398 </pre> |
|
399 |
|
400 <p> |
|
401 |
|
402 We can now proceed and actually implement the creation of the information view: |
|
403 </p> |
|
404 |
|
405 <pre> |
|
406 |
|
407 // Creates the info view. |
|
408 function createInfoView() { |
|
409 // empty caption text to display the caption bar - custom background using CSS |
|
410 infoView = new ListView(null, ""); |
|
411 |
|
412 // home city time |
|
413 homeCityTimePanel = new ContentPanel(); |
|
414 infoView.addControl(homeCityTimePanel); |
|
415 |
|
416 // local city time |
|
417 localCityTimePanel = new ContentPanel(); |
|
418 infoView.addControl(localCityTimePanel); |
|
419 |
|
420 // separator |
|
421 infoView.addControl(new Separator()); |
|
422 |
|
423 // home city weather |
|
424 homeCityWeatherPanel = new ContentPanel(); |
|
425 infoView.addControl(homeCityWeatherPanel); |
|
426 |
|
427 // local city weather |
|
428 localCityWeatherPanel = new ContentPanel(); |
|
429 infoView.addControl(localCityWeatherPanel); |
|
430 |
|
431 // separator |
|
432 infoView.addControl(new Separator()); |
|
433 |
|
434 // news headlines |
|
435 newsHeadlinesPanel = new ContentPanel(); |
|
436 infoView.addControl(newsHeadlinesPanel); |
|
437 } |
|
438 </pre> |
|
439 |
|
440 <p> |
|
441 |
|
442 We already have a function that turns a weather forecast into HTML but we |
|
443 don't have similar functions for time or for news headlines. We'll need |
|
444 that when we want to put content into the time and headline content panels |
|
445 so let's write those functions next. |
|
446 </p> |
|
447 |
|
448 <p> |
|
449 |
|
450 For time we'll need a function that takes a DateTime object and returns |
|
451 HTML that we can use in the content panel in the information view. |
|
452 </p> |
|
453 |
|
454 <pre> |
|
455 |
|
456 // Returns HTML for time. |
|
457 function getHTMLForTime(time) { |
|
458 // build HTML buffer |
|
459 var timeBuf = ""; |
|
460 timeBuf += "<div class=\"Clock\">"; |
|
461 timeBuf += time.hours + ":" + (time.minutes < 10 ? "0" : "") + time.minutes; |
|
462 timeBuf += "</div>"; |
|
463 return timeBuf; |
|
464 } |
|
465 </pre> |
|
466 |
|
467 <p> |
|
468 |
|
469 The HTML uses a CSS rule that we'll have to define in our stylesheet: |
|
470 </p> |
|
471 |
|
472 <pre> |
|
473 |
|
474 /* Clock div */ |
|
475 .Clock { |
|
476 text-align: center; |
|
477 font-size: 16px; |
|
478 font-weight: bold; |
|
479 } |
|
480 </pre> |
|
481 |
|
482 <p> |
|
483 |
|
484 Next up is the function for turning the latest news headlines into HTML. |
|
485 For each news headline we'll generate a div that contains the actual |
|
486 news headline plus a link to the website where the full news article is. |
|
487 Opening URLs should be done using the widget.openURL() method in the |
|
488 S60 Web Runtime but since we don't have that functionality in a PC browser |
|
489 we'll write a wrapper for the function just like we did for the RSS Reader: |
|
490 </p> |
|
491 |
|
492 <pre> |
|
493 |
|
494 // Opens a URL. |
|
495 function openURL(url) { |
|
496 if (window.widget) { |
|
497 // in WRT |
|
498 widget.openURL(url); |
|
499 } else { |
|
500 // outside WRT |
|
501 window.open(url, "NewWindow"); |
|
502 } |
|
503 } |
|
504 </pre> |
|
505 |
|
506 <p> |
|
507 |
|
508 Now we can implement the function that turns news headlines into HTML: |
|
509 </p> |
|
510 |
|
511 <pre> |
|
512 |
|
513 // Returns HTML for news headlines. |
|
514 function getHTMLForNewsHeadlines(newsHeadlines) { |
|
515 var newsBuf = ""; |
|
516 for (var i = 0; i < newsHeadlines.length; i++) { |
|
517 newsBuf += "<div class=\"NewsHeadline\">"; |
|
518 newsBuf += newsHeadlines[i].headline + "<br/>"; |
|
519 newsBuf += "<a href=\"JavaScript:openURL('" + newsHeadlines[i].url + "');\">"; |
|
520 newsBuf += "Read more..."; |
|
521 newsBuf += "</a>"; |
|
522 newsBuf += "</div>"; |
|
523 } |
|
524 return newsBuf; |
|
525 } |
|
526 </pre> |
|
527 |
|
528 <p> |
|
529 |
|
530 The div that encloses each news headline uses a CSS rule called NewsHeadline. |
|
531 As with all other CSS rules that we use we'll define this one too in |
|
532 the TravelCompanion.css stylesheet file. We also need to add rules for what |
|
533 links should look like in the context of a news headline. We define the link |
|
534 as bold underlined blue text in its normal state and inverse with blue background |
|
535 and white text when focused. |
|
536 </p> |
|
537 |
|
538 <pre> |
|
539 |
|
540 /* News headline */ |
|
541 .NewsHeadline { |
|
542 padding: 0px 0px 10px 0px; |
|
543 } |
|
544 |
|
545 /* Anchor tags in the context of a news headline link */ |
|
546 .NewsHeadline a { |
|
547 text-decoration: underline; |
|
548 font-weight: bold; |
|
549 color: rgb(0,0,255); |
|
550 } |
|
551 |
|
552 /* Focused anchor tags */ |
|
553 .NewsHeadline a:focus { |
|
554 background: rgb(0,0,255); |
|
555 color: rgb(255,255,255); |
|
556 } |
|
557 </pre> |
|
558 |
|
559 <p> |
|
560 |
|
561 Now we should have everything we need to be able to update the information |
|
562 view content panels when the view is shown. It's time to implement the |
|
563 showInfoView() function: |
|
564 </p> |
|
565 |
|
566 <pre> |
|
567 |
|
568 // Displays the info view. |
|
569 function showInfoView() { |
|
570 // set current information to controls |
|
571 var homeCity = engine.getHomeCity(); |
|
572 var localCity = engine.getLocalCity(); |
|
573 |
|
574 // set time |
|
575 var homeTime = engine.getHomeTime(); |
|
576 var localTime = engine.getLocalTime(); |
|
577 homeCityTimePanel.setCaption(homeCity.name + " Time"); |
|
578 homeCityTimePanel.setContent(getHTMLForTime(homeTime)); |
|
579 localCityTimePanel.setCaption(localCity.name + " Time"); |
|
580 localCityTimePanel.setContent(getHTMLForTime(localTime)); |
|
581 |
|
582 // set weather |
|
583 var homeWeather = engine.getHomeWeather(); |
|
584 var localWeather = engine.getLocalWeather(); |
|
585 homeCityWeatherPanel.setCaption(homeCity.name + " Weather"); |
|
586 homeCityWeatherPanel.setContent(getHTMLForWeather(homeWeather[0])); |
|
587 localCityWeatherPanel.setCaption(localCity.name + " Weather"); |
|
588 localCityWeatherPanel.setContent(getHTMLForWeather(localWeather[0])); |
|
589 |
|
590 // set headline |
|
591 var newsHeadlines = engine.getNewsHeadlines(); |
|
592 newsHeadlinesPanel.setCaption("News Headlines"); |
|
593 newsHeadlinesPanel.setContent(getHTMLForNewsHeadlines(newsHeadlines)); |
|
594 |
|
595 setSubViewSoftkeys(); |
|
596 uiManager.setView(infoView); |
|
597 } |
|
598 </pre> |
|
599 |
|
600 <p> |
|
601 |
|
602 If you try the view now, everything will seem to work fine. But there |
|
603 is a big problem! We set the content in the content panels when the |
|
604 view is shown but if we stay in the view for an hour the clocks will |
|
605 still show the same time as when we showed the view. What's worse, it's |
|
606 not just the information view that has this problem. The weather forecast |
|
607 view is equally broken. |
|
608 </p> |
|
609 |
|
610 </div> |
|
611 |
|
612 <div class="section"><h2 class="sectiontitle"> |
|
613 Updating views with a timer</h2> |
|
614 |
|
615 |
|
616 <p> |
|
617 |
|
618 In order to keep the views up to date we'll need to start a timer that |
|
619 periodically calls a function where we examine whether it's been long |
|
620 enough since the last update of a view that it's time to update the |
|
621 content in that view. Note that we only need to update a view using the |
|
622 timer if the view is actually visible. |
|
623 </p> |
|
624 |
|
625 <p> |
|
626 |
|
627 That sounds good at first but some things have to be updated more |
|
628 frequently than others. The clocks in our information view have to not |
|
629 only be updated once a minute but immediately when the minute changes. |
|
630 Such accuracy is not needed for the weather forecasts or news headlines. |
|
631 For them it's fine if they are updated once per hour. Except that weather |
|
632 forecasts also have to be updated when the day rolls over to the next as |
|
633 otherwise the forecast will show the wrong day. Because the home and local |
|
634 cities can be in different timezones their days will roll over at |
|
635 different times and thus we have to track these separately. |
|
636 </p> |
|
637 |
|
638 <p> |
|
639 |
|
640 It turns out that we need no less than five variables to track this. |
|
641 We need one to track the last minute that has been updated to the clocks, |
|
642 one variable is needed to track when the weather was last updated, one |
|
643 to track the day in the home city when the home city weather forecast |
|
644 was updated, one to track the same but for the local city, and one to |
|
645 track when the news headlines were updated. Let's declare those variables |
|
646 and initialize them: |
|
647 </p> |
|
648 |
|
649 <pre> |
|
650 |
|
651 // Tracks last updated minute on the clocks. |
|
652 var lastUpdatedClockMinute = -1; |
|
653 |
|
654 // Tracks last update time for weather. |
|
655 var weatherLastUpdated = -1; |
|
656 |
|
657 // Tracks the days when the weather was last updated. |
|
658 var lastUpdatedHomeWeatherDay = -1; |
|
659 var lastUpdatedLocalWeatherDay = -1; |
|
660 |
|
661 // Tracks last update time for news headlines. |
|
662 var newsHeadlinesLastUpdated = -1; |
|
663 </pre> |
|
664 |
|
665 <p> |
|
666 |
|
667 We'll use the time in millieconds to remember update times and compare |
|
668 how long it's been since an update happened. Since we'll use this quite |
|
669 a lot, let's create a small helper function for it: |
|
670 </p> |
|
671 |
|
672 <pre> |
|
673 |
|
674 // Returns the current time in milliseconds. |
|
675 function getTimeMillis() { |
|
676 return new Date().getTime(); |
|
677 } |
|
678 </pre> |
|
679 |
|
680 <p> |
|
681 |
|
682 When we show a view we have to update the appropriate tracking variables |
|
683 so the widget will properly keep track of when a view was updated. That |
|
684 means that we have to add the following to just before we show the |
|
685 information view in showInfoView(): |
|
686 </p> |
|
687 |
|
688 <pre> |
|
689 |
|
690 // update the tracking variables |
|
691 lastUpdatedClockMinute = homeTime.minutes; |
|
692 weatherLastUpdated = getTimeMillis(); |
|
693 lastUpdatedHomeWeatherDay = homeTime.day; |
|
694 lastUpdatedLocalWeatherDay = localTime.day; |
|
695 newsHeadlinesLastUpdated = getTimeMillis(); |
|
696 </pre> |
|
697 |
|
698 <p> |
|
699 |
|
700 In the same way the showWeatherView() function needs the following addition: |
|
701 </p> |
|
702 |
|
703 <pre> |
|
704 |
|
705 // update the tracking variables |
|
706 weatherLastUpdated = getTimeMillis(); |
|
707 lastUpdatedLocalWeatherDay = engine.getLocalTime().day; |
|
708 </pre> |
|
709 |
|
710 <p> |
|
711 |
|
712 Now our tracking variables should be updated to the right times whenever views |
|
713 are shown but we still don't have a timer to handle the automatic updating |
|
714 of the views. |
|
715 </p> |
|
716 |
|
717 <p> |
|
718 |
|
719 We'll set this timer up so that the timer callback function is called once |
|
720 every second so that we can make sure that the clocks don't fall behind. We'll |
|
721 track the timer identifier in a variable: |
|
722 </p> |
|
723 |
|
724 <pre> |
|
725 |
|
726 // View updating timer identifier. |
|
727 var timerId; |
|
728 </pre> |
|
729 |
|
730 <p> |
|
731 |
|
732 We'll make the timer call a function called updateViews() that we will |
|
733 create shortly but first let's start the timer as the last thing in the |
|
734 init() function: |
|
735 </p> |
|
736 |
|
737 <pre> |
|
738 |
|
739 // start timer that keeps views up to date |
|
740 timerId = setInterval(updateViews, 1000); |
|
741 </pre> |
|
742 |
|
743 <p> |
|
744 |
|
745 The updateViews() function needs to check what view we are currently in and do |
|
746 the appropriate checks depending on the view. If we're in the information view |
|
747 then we have three things to check. The first thing is to check if the minute |
|
748 has changed on the clock. The second is to check if it's been over 60 minutes |
|
749 since the weather was updated or if either the home or local city has rolled |
|
750 over to the next day. And finally the third to thing to check is if it's been |
|
751 over 60 minutes since the news headlines were updated. |
|
752 </p> |
|
753 |
|
754 <p> |
|
755 |
|
756 If we are in the weather view then we check if it's been over 60 minutes since |
|
757 the weather was updated or if the local city has rolled over to the next day. |
|
758 Note that the weather forecast view only displays the local weather forecast |
|
759 so we don't have to check for if the home city has rolled over to the next day. |
|
760 </p> |
|
761 |
|
762 <p> |
|
763 |
|
764 The implementation of the function is therefore as follows: |
|
765 </p> |
|
766 |
|
767 <pre> |
|
768 |
|
769 // Timer callback function that gets called once every second to keep views up to date. |
|
770 function updateViews() { |
|
771 |
|
772 // get the current view |
|
773 var currentView = uiManager.getView(); |
|
774 |
|
775 // get home and local time as well as time in milliseconds |
|
776 var homeTime = engine.getHomeTime(); |
|
777 var localTime = engine.getLocalTime(); |
|
778 var now = getTimeMillis(); |
|
779 |
|
780 if (currentView == infoView) { |
|
781 // only update the clocks if the minute has changed |
|
782 if (homeTime.minutes != lastUpdatedClockMinute) { |
|
783 lastUpdatedClockMinute = homeTime.minute; |
|
784 homeCityTimePanel.setContent(getHTMLForTime(homeTime)); |
|
785 localCityTimePanel.setContent(getHTMLForTime(localTime)); |
|
786 } |
|
787 |
|
788 // update weather if it hasn't been updated in the last hour or if the day has changed |
|
789 if ((now > weatherLastUpdated + (1000 * 60 * 60)) || |
|
790 (homeTime.day != lastUpdatedHomeWeatherDay) || |
|
791 (localTime.day != lastUpdatedLocalWeatherDay)) { |
|
792 weatherLastUpdated = now; |
|
793 lastUpdatedHomeWeatherDay = homeTime.day; |
|
794 lastUpdatedLocalWeatherDay = localTime.day; |
|
795 var homeWeather = engine.getHomeWeather(); |
|
796 var localWeather = engine.getLocalWeather(); |
|
797 homeCityWeatherPanel.setContent(getHTMLForWeather(homeWeather[0])); |
|
798 localCityWeatherPanel.setContent(getHTMLForWeather(localWeather[0])); |
|
799 } |
|
800 |
|
801 // update news headlines if they haven't been updated in the last hour |
|
802 if (now > newsHeadlinesLastUpdated + (1000 * 60 * 60)) { |
|
803 newsHeadlinesLastUpdated = now; |
|
804 var newsHeadlines = engine.getNewsHeadlines(); |
|
805 newsHeadlinesPanel.setContent(getHTMLForNewsHeadlines(newsHeadlines)); |
|
806 } |
|
807 } else if (currentView == weatherView) { |
|
808 // update weather if it hasn't been updated in the last hour or if the day has changed |
|
809 if ((now > weatherLastUpdated + (1000 * 60 * 60)) || |
|
810 (localTime.day != lastUpdatedLocalWeatherDay)) { |
|
811 weatherLastUpdated = now; |
|
812 lastUpdatedLocalWeatherDay = localTime.day; |
|
813 updateWeatherForecast(); |
|
814 } |
|
815 } |
|
816 } |
|
817 </pre> |
|
818 |
|
819 <div class="fignone" id="GUID-384F2430-3DE9-4006-AC8E-86C5774C3A79__GUID-CBE870F8-DE6F-47D9-BE2B-F7D756DC8A40"><a name="GUID-384F2430-3DE9-4006-AC8E-86C5774C3A79__GUID-CBE870F8-DE6F-47D9-BE2B-F7D756DC8A40"><!-- --></a><span class="figcap">Figure 2. |
|
820 Travel Companion information view</span> |
|
821 |
|
822 |
|
823 <br /><img src="Travel_Companion_Info_Screenshot_1.png" /><br /> |
|
824 </div> |
|
825 |
|
826 </div> |
|
827 |
|
828 <div class="section"><h2 class="sectiontitle"> |
|
829 What we have learned</h2> |
|
830 |
|
831 |
|
832 <p> |
|
833 |
|
834 The Travel Companion widget is now completed! We have learned to implement |
|
835 complex widgets with multiple views using the WRTKit. We have also learned |
|
836 how to keep information in views up to date using a timer. And we implemented |
|
837 the widget without any dependencies to how the business logic engine was |
|
838 implemented. That will come in handy if you want to continue developing this |
|
839 widget by substituting the mock engine with one that actually uses real |
|
840 world data. You are now ready to tackle any challenge using the WRTKit! |
|
841 </p> |
|
842 |
|
843 </div> |
|
844 |
|
845 </div> |
|
846 |
|
847 <div> |
|
848 <div class="familylinks"> |
|
849 <div class="parentlink"><strong>Parent topic:</strong> <a href="WRTKit_Travel_Companion_Tutorial-GUID-be79ba64-fa03-4968-964e-d7dcc42d7053.html">Travel Companion</a></div> |
|
850 </div> |
|
851 </div> |
|
852 |
|
853 </body> |
|
854 </html> |