5e1b7359ff5cb1123ca005d990c896e726aecc93
[pedometerwidget] / src / usr / lib / hildon-desktop / pedometer_widget_home.py
1 #Pedometer Home Widget
2 #Author: Mirestean Andrei < andrei.mirestean at gmail.com >
3 #
4 #This program is free software: you can redistribute it and/or modify
5 #it under the terms of the GNU General Public License as published by
6 #the Free Software Foundation, either version 3 of the License, or
7 #(at your option) any later version.
8 #
9 #This program is distributed in the hope that it will be useful,
10 #but WITHOUT ANY WARRANTY; without even the implied warranty of
11 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 #GNU General Public License for more details.
13 #
14 #You should have received a copy of the GNU General Public License
15 #along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 import os
18 import time
19 import pickle
20 from datetime import date, timedelta
21 from xml.dom.minidom import getDOMImplementation, parseString
22
23 import gobject
24 import gconf
25 import gtk
26 import cairo
27
28 import hildondesktop
29 import hildon
30
31 PATH = "/apps/pedometerhomewidget"
32 MODE = PATH + "/mode"
33 HEIGHT = PATH + "/height"
34 UNIT = PATH + "/unit"
35 ASPECT = PATH + "/aspect"
36 SECONDVIEW = PATH + "/secondview"
37 GRAPHVIEW = PATH + "/graphview"
38 NOIDLETIME = PATH + "/noidletime"
39 LOGGING = PATH + "/logging"
40
41 ALARM_PATH = PATH + "/alarm"
42 ALARM_ENABLE = ALARM_PATH + "/enable"
43 ALARM_FNAME = ALARM_PATH + "/fname"
44 ALARM_TYPE = ALARM_PATH + "/type"
45 ALARM_INTERVAL = ALARM_PATH + "/interval"
46
47 ICONSPATH = "/opt/pedometerhomewidget/"
48
49 unit = 0
50
51 class Singleton(object):
52     _instance = None
53     def __new__(cls, *args, **kwargs):
54         if not cls._instance:
55             cls._instance = super(Singleton, cls).__new__(
56                                 cls, *args, **kwargs)
57         return cls._instance
58
59 class PedoIntervalCounter(Singleton):
60     MIN_THRESHOLD = 500
61     MIN_TIME_STEPS = 0.5
62     x = []
63     y = []
64     z = []
65     t = []
66
67     #TODO: check if last detected step is at the end of the interval
68
69     def set_vals(self, coords, tval):
70         self.x = coords[0]
71         self.y = coords[1]
72         self.z = coords[2]
73         self.t = tval
74
75     def set_mode(self, mode):
76         #runnig, higher threshold to prevent fake steps
77         self.mode = mode
78         if mode == 1:
79             self.MIN_THRESHOLD = 650
80             self.MIN_TIME_STEPS = 0.35
81         #walking
82         else:
83             self.MIN_THRESHOLD = 500
84             self.MIN_TIME_STEPS = 0.5
85
86     def calc_mean(self, vals):
87         sum = 0
88         for i in vals:
89             sum += i
90         if len(vals) > 0:
91             return sum / len(vals)
92         return 0
93
94     def calc_stdev(self, vals):
95         rez = 0
96         mean = self.calc_mean(vals)
97         for i in vals:
98             rez += pow(abs(mean - i), 2)
99         return math.sqrt(rez / len(vals))
100
101     def calc_threshold(self, vals):
102         vmax = max(vals)
103         vmin = min(vals)
104         mean = self.calc_mean(vals)
105         threshold = max (abs(mean - vmax), abs(mean - vmin))
106         return threshold
107
108     def count_steps(self, vals, t):
109         threshold = self.MIN_THRESHOLD
110         mean = self.calc_mean(vals)
111         cnt = 0
112         i = 0
113         while i < len(vals):
114             if abs(vals[i] - mean) > threshold:
115                 cnt += 1
116                 ntime = t[i] + self.MIN_TIME_STEPS
117                 while i < len(vals) and t[i] < ntime:
118                     i += 1
119             i += 1
120         return cnt
121
122     def get_best_values(self, x, y, z):
123         dev1 = self.calc_stdev(x)
124         dev2 = self.calc_stdev(y)
125         dev3 = self.calc_stdev(z)
126         dev_max = max(dev1, dev2, dev3)
127
128         if (abs(dev1 - dev_max) < 0.001):
129             logger.info("X chosen as best axis, stdev %f" % dev1)
130             return x
131         elif (abs(dev2 - dev_max) < 0.001):
132             logger.info("Y chosen as best axis, stdev %f" % dev2)
133             return y
134         else:
135             logger.info("Z chosen as best axis, stdev %f" % dev3)
136             return z
137
138     def number_steps(self):
139         vals = self.get_best_values(self.x, self.y, self.z)
140         return self.count_steps(vals, self.t)
141
142 class PedoValues():
143     def __init__(self, time=0, steps=0, dist=0, calories=0):
144         self.time = time
145         self.steps = steps
146         self.calories = calories
147         self.dist = dist
148         self.unit = unit
149
150     def __add__(self, other):
151         return PedoValues(self.time + other.time,
152                           self.steps + other.steps,
153                           self.dist + other.dist,
154                           self.calories + other.calories)
155
156     def __sub__(self, other):
157         return PedoValues(self.time - other.time,
158                           self.steps - other.steps,
159                           self.dist - other.dist,
160                           self.calories - other.calories)
161
162     def get_print_time(self):
163         tdelta = self.time
164         hours = int(tdelta / 3600)
165         tdelta -= 3600 * hours
166         mins = int(tdelta / 60)
167         tdelta -= 60 * mins
168         secs = int(tdelta)
169         strtime = "%.2d:%.2d:%.2d" % (hours, mins, secs)
170         return strtime
171
172     def get_print_distance(self):
173         if self.dist > 1000:
174             if self.unit == 0:
175                 return "%.2f km" % (self.dist / 1000)
176             else:
177                 return "%.2f mi" % (self.dist / 1609.344)
178         else:
179             if self.unit == 0:
180                 return "%d m" % self.dist
181             else:
182                 return "%d ft" % int(self.dist * 3.2808)
183
184     def get_avg_speed(self):
185         conv = 0
186         if self.unit:
187             conv = 2.23693629
188         else:
189             conv = 3.6
190
191         if self.time == 0:
192             return 0
193         speed = 1.0 * self.dist / self.time
194         return speed * conv
195
196     def get_print_avg_speed(self):
197         suffix = ""
198         conv = 0
199         if self.unit:
200             suffix = "mi/h"
201             conv = 2.23693629
202         else:
203             suffix = "km/h"
204             conv = 3.6
205
206         if self.time == 0:
207             return "N/A " + suffix
208         speed = 1.0 * self.dist / self.time
209         #convert from meters per second to km/h or mi/h
210         speed *= conv
211         return "%.2f %s" % (speed, suffix)
212
213     def get_print_steps(self):
214         return str(self.steps)
215
216     def get_print_calories(self):
217         return str(self.calories)
218
219 class PedoRepository(Singleton):
220     values = {}
221
222     def load(self):
223         raise NotImplementedError("Must be implemented by subclass")
224
225     def save(self):
226         raise NotImplementedError("Must be implemented by subclass")
227
228     def reset_values(self):
229         self.values = {}
230         self.save()
231
232     def get_history_count(self):
233         """return the number of days in the log"""
234         return len(values)
235
236     def get_values(self):
237         return self.values
238
239     def add_values(self, values, when=date.today()):
240         """add PedoValues values to repository """
241         try:
242             self.values[when] = self.values[when] + values
243         except KeyError:
244             self.values[when] = values
245
246     def get_last_7_days(self):
247         ret = []
248         day = date.today()
249         for i in range(7):
250             try:
251                 ret.append(self.values[day])
252             except KeyError:
253                 ret.append(PedoValues())
254             day = day - timedelta(days=1)
255         return ret
256
257     def get_last_weeks(self):
258         delta = timedelta(days=1)
259         day = date.today()
260         week = int(date.today().strftime("%W"))
261         val = PedoValues()
262         ret = []
263         for i in range(56):
264             try:
265                 val += self.values[day]
266             except KeyError:
267                 pass
268             w = int(day.strftime("%W"))
269             if w != week:
270                 ret.append(val)
271                 val = PedoValues()
272                 week = w
273                 if len(ret) == 7:
274                     break
275             day -= delta
276         return ret
277
278     def get_alltime_values(self):
279         ret = PedoValues()
280         for k, v in self.values.iteritems():
281             ret = ret + v
282         return ret
283
284     def get_today_values(self):
285         try:
286             return self.values[date.today()]
287         except KeyError:
288             return PedoValues()
289
290     def get_this_week_values(self):
291         day = date.today()
292         ret = PedoValues()
293         while True:
294             try:
295                 ret += self.values[day]
296             except:
297                 pass
298             if day.weekday() == 0:
299                 break
300             day = day - timedelta(days=1)
301
302         return ret
303
304 class PedoRepositoryXML(PedoRepository):
305     DIR = os.path.join(os.path.expanduser("~"), ".pedometer")
306     FILE = os.path.join(DIR, "data.xml")
307     FILE2 = os.path.join(DIR, "pickle.log")
308     def __init__(self):
309         if not os.path.exists(self.DIR):
310             os.makedirs(self.DIR)
311         PedoRepository.__init__(self)
312
313     def load(self):
314         try:
315             f = open(self.FILE, "r")
316             dom = parseString(f.read())
317             values = dom.getElementsByTagName("pedometer")[0]
318             for v in values.getElementsByTagName("date"):
319                 d = int(v.getAttribute("ordinal_day"))
320                 steps = int(v.getAttribute("steps"))
321                 calories = float(v.getAttribute("calories"))
322                 dist = float(v.getAttribute("dist"))
323                 time = float(v.getAttribute("time"))
324                 day = date.fromordinal(d)
325                 self.values[day] = PedoValues(time, steps, dist, calories)
326
327             f.close()
328         except Exception, e:
329             logger.error("Error while loading data from xml file: %s" % e)
330
331     def save(self):
332         try:
333             f = open(self.FILE, "w")
334
335             impl = getDOMImplementation()
336
337             newdoc = impl.createDocument(None, "pedometer", None)
338             top_element = newdoc.documentElement
339             for k, v in self.values.iteritems():
340                 d = newdoc.createElement('date')
341                 d.setAttribute("day", str(k.isoformat()))
342                 d.setAttribute("ordinal_day", str(k.toordinal()))
343                 d.setAttribute("steps", str(v.steps))
344                 d.setAttribute("time", str(v.time))
345                 d.setAttribute("dist", str(v.dist))
346                 d.setAttribute("calories", str(v.calories))
347                 top_element.appendChild(d)
348
349             newdoc.appendChild(top_element)
350             newdoc.writexml(f)
351             #f.write(newdoc.toprettyxml())
352             f.close()
353         except Exception, e:
354             logger.error("Error while saving data to xml file: %s" % e)
355
356 class PedoRepositoryPickle(PedoRepository):
357     DIR = os.path.join(os.path.expanduser("~"), ".pedometer")
358     FILE = os.path.join(DIR, "pickle.log")
359
360     def __init__(self):
361         if not os.path.exists(self.DIR):
362             os.makedirs(self.DIR)
363         PedoRepository.__init__(self)
364
365     def load(self):
366         try:
367             f = open(self.FILE, "rb")
368             self.values = pickle.load(f)
369             f.close()
370         except Exception, e:
371             logger.error("Error while loading pickle file: %s" % e)
372
373     def save(self):
374         try:
375             f = open(self.FILE, "wb")
376             pickle.dump(self.values, f)
377             f.close()
378         except Exception, e:
379             logger.error("Error while saving data to pickle: %s" % e)
380
381 class AlarmController(Singleton):
382     enable = False
383     fname = "/home/user/MyDocs/.sounds/Ringtones/Bicycle.aac"
384     interval = 5
385     type = 0
386
387     def __init__(self):
388         self.client = gconf.client_get_default()
389         try:
390             self.enable = self.client.get_bool(ALARM_ENABLE)
391             self.fname = self.client.get_string(ALARM_FNAME)
392             self.interval = self.client.get_int(ALARM_INTERVAL)
393             self.type = self.client.get_int(ALARM_TYPE)
394         except:
395             self.client.set_bool(ALARM_ENABLE, self.enable)
396             self.client.set_string(ALARM_FNAME, self.fname)
397             self.client.set_int(ALARM_INTERVAL, self.interval)
398             self.client.set_int(ALARM_TYPE, self.type)
399
400     def update(self):
401         pass
402
403     def play(self):
404         pass
405
406     def set_enable(self, value):
407        self.enable = value
408        self.client.set_bool(ALARM_ENABLE, value)
409
410     def get_enable(self):
411         return self.enable
412
413     def set_alarm_file(self, fname):
414         self.fname = fname
415         self.client.set_string(ALARM_FNAME, fname)
416
417     def get_alarm_file(self):
418         if self.fname == None:
419             return ""
420         return self.fname
421
422     def set_interval(self, interval):
423         self.interval = interval
424         self.client.set_int(ALARM_INTERVAL, interval)
425
426     def get_interval(self):
427         return self.interval
428
429     def set_type(self, type):
430         self.type = type
431         self.client.set_int(ALARM_TYPE, type)
432
433     def get_type(self):
434         return self.type
435
436 class PedoController(Singleton):
437     mode = 0
438     unit = 0
439     height_interval = 0
440     #what to display in second view - 0 - alltime, 1 - today, 2 - week
441     second_view = 0
442     callback_update_ui = None
443     no_idle_time = False
444
445     STEP_LENGTH = 0.7
446     #values for the two views in the widget ( current and day/week/alltime)
447     v = [PedoValues(), PedoValues()]
448
449     last_time = 0
450     is_running = False
451
452     observers = []
453
454     def __init__(self):
455         self.pedometer = PedoCounter(self.steps_detected)
456         self.pedometerInterval = PedoIntervalCounter()
457         self.pedometerInterval.set_mode(self.mode)
458         self.repository = PedoRepositoryXML()
459         self.repository.load()
460
461         self.load_values()
462
463     def load_values(self):
464         if self.second_view == 0:
465             self.v[1] = self.repository.get_alltime_values()
466         elif self.second_view == 1:
467             self.v[1] = self.repository.get_today_values()
468         else:
469             self.v[1] = self.repository.get_this_week_values()
470
471     def save_values(self):
472         self.repository.add_values(self.v[0])
473         self.repository.save()
474         self.load_values()
475
476     def start_pedometer(self):
477         self.v[0] = PedoValues()
478         self.last_time = time.time()
479         self.is_running = True
480         self.pedometer.start()
481         self.notify(True)
482
483     def stop_pedometer(self):
484         self.is_running = False
485         self.pedometer.request_stop()
486
487     def get_first(self):
488         return self.v[0]
489
490     def get_second(self):
491         if self.is_running:
492             return self.v[0] + self.v[1]
493         else:
494             return self.v[1]
495
496     def update_current(self):
497         """
498         Update distance and calories for current values based on new height, mode values
499         """
500         self.v[0].dist = self.get_distance(self.v[0].steps)
501         self.v[0].calories = self.get_calories(self.v[0].steps)
502
503     def steps_detected(self, cnt, last_steps=False):
504         if not last_steps and cnt == 0 and self.no_idle_time:
505             logger.info("No steps detected, timer is paused")
506         else:
507             self.v[0].steps += cnt
508             self.v[0].dist += self.get_distance(cnt)
509             self.v[0].calories += self.get_distance(cnt)
510             self.v[0].time += time.time() - self.last_time
511             if last_steps:
512                 self.save_values()
513                 self.notify()
514             else:
515                 self.notify(True)
516         self.last_time = time.time()
517
518     def set_mode(self, mode):
519         self.mode = mode
520         self.set_height(self.height_interval)
521         self.notify()
522
523     def set_unit(self, new_unit):
524         self.unit = new_unit
525         unit = new_unit
526
527     def set_second_view(self, second_view):
528         self.second_view = second_view
529         self.load_values()
530         self.notify()
531
532     def set_callback_ui(self, func):
533         self.callback_update_ui = func
534
535     def set_height(self, height_interval):
536         self.height_inteval = height_interval
537         #set height, will affect the distance
538         if height_interval == 0:
539             self.STEP_LENGTH = 0.59
540         elif height_interval == 1:
541             self.STEP_LENGTH = 0.64
542         elif height_interval == 2:
543             self.STEP_LENGTH = 0.71
544         elif height_interval == 3:
545             self.STEP_LENGTH = 0.77
546         elif height_interval == 4:
547             self.STEP_LENGTH = 0.83
548         #increase step length if RUNNING
549         if self.mode == 1:
550             self.STEP_LENGTH *= 1.45
551         self.notify()
552
553     def set_no_idle_time(self, value):
554         self.no_idle_time = value
555
556     def get_distance(self, steps=None):
557         if steps == None:
558             steps = self.counter
559         return self.STEP_LENGTH * steps;
560
561     def get_calories(self, steps):
562         return steps
563
564     def add_observer(self, func):
565         try:
566             self.observers.index(func)
567         except:
568             self.observers.append(func)
569
570     def remove_observer(self, func):
571         self.observers.remove(func)
572
573     def notify(self, optional=False):
574         if self.callback_update_ui is not None:
575             self.callback_update_ui()
576
577         for func in self.observers:
578             func(optional)
579
580
581 class PedoCounter(Singleton):
582     COORD_FNAME = "/sys/class/i2c-adapter/i2c-3/3-001d/coord"
583     COORD_FNAME_SDK = "/home/andrei/pedometer-widget-0.1/date.txt"
584     LOGFILE = "/home/user/log_pedometer"
585     #time in ms between two accelerometer data reads
586     COORD_GET_INTERVAL = 10
587
588     COUNT_INTERVAL = 5
589
590     interval_counter = None
591     stop_requested = False
592     update_function = None
593     logging = False
594     isRunning = False
595
596     def __init__(self, update_function=None):
597         if not os.path.exists(self.COORD_FNAME):
598             self.COORD_FNAME = self.COORD_FNAME_SDK
599
600         self.interval_counter = PedoIntervalCounter()
601         self.update_function = update_function
602
603     def set_logging(self, value):
604         self.logging = value
605
606     def get_rotation(self):
607         f = open(self.COORD_FNAME, 'r')
608         coords = [int(w) for w in f.readline().split()]
609         f.close()
610         return coords
611
612     def start(self):
613         logger.info("Counter started")
614         self.isRunning = True
615         self.stop_requested = False
616         if self.logging:
617             fname = "%d_%d_%d_%d_%d_%d" % time.localtime()[0:6]
618             self.file = open(self.LOGFILE + fname + ".txt", "w")
619         gobject.idle_add(self.run)
620
621     def run(self):
622         self.coords = [[], [], []]
623         self.stime = time.time()
624         self.t = []
625         gobject.timeout_add(self.COORD_GET_INTERVAL, self.read_coords)
626         return False
627
628     def read_coords(self):
629         x, y, z = self.get_rotation()
630         self.coords[0].append(int(x))
631         self.coords[1].append(int(y))
632         self.coords[2].append(int(z))
633         now = time.time() - self.stime
634         if self.logging:
635             self.file.write("%d %d %d %f\n" % (self.coords[0][-1], self.coords[1][-1], self.coords[2][-1], now))
636
637         self.t.append(now)
638         #call stop_interval
639         ret = True
640         if self.t[-1] > self.COUNT_INTERVAL or self.stop_requested:
641             ret = False
642             gobject.idle_add(self.stop_interval)
643         return ret
644
645     def stop_interval(self):
646         self.interval_counter.set_vals(self.coords, self.t)
647         cnt = self.interval_counter.number_steps()
648
649         logger.info("Number of steps detected for last interval %d, number of coords: %d" % (cnt, len(self.t)))
650
651         gobject.idle_add(self.update_function, cnt, self.stop_requested)
652
653         if self.stop_requested:
654             gobject.idle_add(self.stop)
655         else:
656             gobject.idle_add(self.run)
657         return False
658
659     def stop(self):
660         if self.logging:
661             self.file.close()
662         logger.info("Counter has finished")
663
664     def request_stop(self):
665         self.stop_requested = True
666         self.isRunning = False
667
668 class CustomButton(hildon.Button):
669     def __init__(self, icon):
670         hildon.Button.__init__(self, gtk.HILDON_SIZE_AUTO_WIDTH, hildon.BUTTON_ARRANGEMENT_VERTICAL)
671         self.icon = icon
672         self.set_size_request(int(32 * 1.4), int(30 * 1.0))
673         self.retval = self.connect("expose_event", self.expose)
674
675     def set_icon(self, icon):
676         self.icon = icon
677
678     def expose(self, widget, event):
679         self.context = widget.window.cairo_create()
680         self.context.rectangle(event.area.x, event.area.y,
681                             event.area.width, event.area.height)
682
683         self.context.clip()
684         rect = self.get_allocation()
685         self.context.rectangle(rect.x, rect.y, rect.width, rect.height)
686         self.context.set_source_rgba(1, 1, 1, 0)
687
688         style = self.rc_get_style()
689         color = style.lookup_color("DefaultBackgroundColor")
690         if self.state == gtk.STATE_ACTIVE:
691             style = self.rc_get_style()
692             color = style.lookup_color("SelectionColor")
693             self.context.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75);
694         self.context.fill()
695
696         #img = cairo.ImageSurface.create_from_png(self.icon)
697
698         #self.context.set_source_surface(img)
699         #self.context.set_source_surface(img, rect.width/2 - img.get_width() /2, 0)
700         img = gtk.Image()
701         img.set_from_file(self.icon)
702         buf = img.get_pixbuf()
703         buf = buf.scale_simple(int(32 * 1.5), int(30 * 1.5), gtk.gdk.INTERP_BILINEAR)
704
705         self.context.set_source_pixbuf(buf, rect.x + (event.area.width / 2 - 15) - 8, rect.y + 1)
706         self.context.scale(200, 200)
707         self.context.paint()
708
709         return self.retval
710
711 class CustomEventBox(gtk.EventBox):
712
713     def __init__(self):
714         gtk.EventBox.__init__(self)
715
716     def do_expose_event(self, event):
717         self.context = self.window.cairo_create()
718         self.context.rectangle(event.area.x, event.area.y,
719                             event.area.width, event.area.height)
720
721         self.context.clip()
722         rect = self.get_allocation()
723         self.context.rectangle(rect.x, rect.y, rect.width, rect.height)
724
725         if self.state == gtk.STATE_ACTIVE:
726             style = self.rc_get_style()
727             color = style.lookup_color("SelectionColor")
728             self.context.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75);
729         else:
730             self.context.set_source_rgba(1, 1, 1, 0)
731         self.context.fill()
732
733         gtk.EventBox.do_expose_event(self, event)
734
735 class GraphController(Singleton):
736     ytitles = ["Steps", "Average Speed", "Distance", "Calories"]
737     xtitles = ["Day", "Week"] # "Today"]
738     widget = None
739     def __init__(self):
740         self.repository = PedoRepositoryXML()
741         self.last_update = 0
742         PedoController().add_observer(self.update_ui)
743
744     def set_graph(self, widget):
745         self.widget = widget
746         self.update_ui()
747
748     def set_current_view(self, view):
749         """
750         current_view % len(ytitles) - gives the ytitle
751         current_view / len(ytitles) - gives the xtitle
752         """
753         self.current_view = view
754
755         if self.current_view == len(self.ytitles) * len(self.xtitles):
756             self.current_view = 0
757         self.x_id = self.current_view / len(self.ytitles)
758         self.y_id = self.current_view % len(self.ytitles)
759
760     def next_view(self):
761         self.set_current_view(self.current_view+1)
762         self.update_ui()
763         return self.current_view
764
765     def last_weeks_labels(self):
766         d = date.today()
767         delta = timedelta(days=7)
768         ret = []
769         for i in range(7):
770             ret.append(d.strftime("Week %W"))
771             d = d - delta
772         return ret
773
774     def compute_values(self):
775         labels = []
776         if self.x_id == 0:
777             values = self.repository.get_last_7_days()
778             d = date.today()
779             delta = timedelta(days=1)
780             for i in range(7):
781                 labels.append(d.ctime().split()[0])
782                 d = d - delta
783
784         elif self.x_id == 1:
785             values = self.repository.get_last_weeks()
786             d = date.today()
787             for i in range(7):
788                 labels.append(d.strftime("Week %W"))
789                 d = d - timedelta(days=7)
790         else:
791             values = self.repository.get_today()
792             #TODO get labels
793
794         if self.y_id == 0:
795             yvalues = [line.steps for line in values]
796         elif self.y_id == 1:
797             yvalues = [line.get_avg_speed() for line in values]
798         elif self.y_id == 2:
799             yvalues = [line.dist for line in values]
800         else:
801             yvalues = [line.calories for line in values]
802
803         #determine values for y lines in graph
804         diff = self.get_best_interval_value(max(yvalues))
805         ytext = []
806         for i in range(6):
807             ytext.append(str(int(i*diff)))
808
809         if self.widget is not None:
810             yvalues.reverse()
811             labels.reverse()
812             self.widget.values = yvalues
813             self.widget.ytext = ytext
814             self.widget.xtext = labels
815             self.widget.max_value = diff * 5
816             self.widget.text = self.xtitles[self.x_id] + " / " + self.ytitles[self.y_id]
817             self.widget.queue_draw()
818         else:
819             logger.error("Widget not set in GraphController")
820
821     def get_best_interval_value(self, max_value):
822         diff =  1.0 * max_value / 5
823         l = len(str(int(diff)))
824         d = math.pow(10, l/2)
825         val = int(math.ceil(1.0 * diff / d)) * d
826         if val == 0:
827             val = 1
828         return val
829
830     def update_ui(self, optional=False):
831         """update graph values every x seconds"""
832         if optional and self.last_update - time.time() < 600:
833             return
834         if self.widget is None:
835             return
836
837         self.compute_values()
838         self.last_update = time.time()
839
840 class GraphWidget(gtk.DrawingArea):
841
842     def __init__(self):
843         gtk.DrawingArea.__init__(self)
844         self.set_size_request(-1, 150)
845         self.yvalues = 5
846
847         """sample values"""
848         self.ytext = ["   0", "1000", "2000", "3000", "4000", "5000"]
849         self.xtext = ["Monday", "Tuesday", "Wednesday","Thursday", "Friday", "Saturday", "Sunday"]
850         self.values = [1500, 3400, 4000, 3600, 3200, 0, 4500]
851         self.max_value = 5000
852         self.text = "All time steps"
853
854     def do_expose_event(self, event):
855         context = self.window.cairo_create()
856
857         # set a clip region for the expose event
858         context.rectangle(event.area.x, event.area.y,
859                                event.area.width, event.area.height)
860         context.clip()
861
862         context.save()
863
864         context.set_operator(cairo.OPERATOR_SOURCE)
865         style = self.rc_get_style()
866
867         if self.state == gtk.STATE_ACTIVE:
868             color = style.lookup_color("SelectionColor")
869         else:
870              color = style.lookup_color("DefaultBackgroundColor")
871         context.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75)
872
873         context.paint()
874         context.restore();
875         self.draw(context)
876
877     def draw(self, cr):
878         space_below = 20
879         space_above = 10
880         border_right = 10
881         border_left = 30
882
883         rect = self.get_allocation()
884         x = rect.width
885         y = rect.height
886
887         cr.select_font_face("Purisa", cairo.FONT_SLANT_NORMAL,
888             cairo.FONT_WEIGHT_NORMAL)
889         cr.set_font_size(13)
890
891         #check space needed to display ylabels
892         te = cr.text_extents(self.ytext[-1])
893         border_left = te[2] + 7
894
895         cr.set_source_rgb(1, 1, 1)
896         cr.move_to(border_left, space_above)
897         cr.line_to(border_left, y-space_below)
898         cr.set_line_width(2)
899         cr.stroke()
900
901         cr.move_to(border_left, y-space_below)
902         cr.line_to(x-border_right, y-space_below)
903         cr.set_line_width(2)
904         cr.stroke()
905
906         ydiff = (y-space_above-space_below) / self.yvalues
907         for i in range(self.yvalues):
908             yy = y-space_below-ydiff*(i+1)
909             cr.move_to(border_left, yy)
910             cr.line_to(x-border_right, yy)
911             cr.set_line_width(0.8)
912             cr.stroke()
913
914
915         for i in range(6):
916             yy = y - space_below - ydiff*i + 5
917             te = cr.text_extents(self.ytext[i])
918
919             cr.move_to(border_left-te[2]-2, yy)
920             cr.show_text(self.ytext[i])
921
922         cr.set_font_size(15)
923         te = cr.text_extents(self.text)
924         cr.move_to((x-te[2])/2, y-5)
925         cr.show_text(self.text)
926
927         graph_x_space = x - border_left - border_right
928         graph_y_space = y - space_below - space_above
929         bar_width = graph_x_space*0.75 / len(self.values)
930         bar_distance = graph_x_space*0.25 / (1+len(self.values))
931
932         #set dummy max value to avoid exceptions
933         if self.max_value == 0:
934             self.max_value = 100
935         for i in range(len(self.values)):
936             xx = border_left + (i+1)*bar_distance + i * bar_width
937             yy = y-space_below
938             height = graph_y_space * (1.0 * self.values[i] / self.max_value)
939             cr.set_source_rgba(1, 1, 1, 0.75)
940             cr.rectangle(int(xx), int(yy-height), int(bar_width), int(height))
941             cr.fill()
942
943         cr.set_source_rgba(1, 1, 1, 1)
944         cr.select_font_face("Purisa", cairo.FONT_SLANT_NORMAL,
945                             cairo.FONT_WEIGHT_NORMAL)
946         cr.set_font_size(13)
947
948         cr.rotate(2*math.pi * (-45) / 180)
949         for i in range(len(self.values)):
950             xx = y - space_below - 10
951             yy = border_left + (i+1)*bar_distance + i * bar_width
952             cr.move_to(-xx, yy + bar_width*1.25 / 2)
953             cr.show_text(self.xtext[i])
954
955 class PedometerHomePlugin(hildondesktop.HomePluginItem):
956     button = None
957
958     #labels to display
959     labels = ["timer", "count", "dist", "avgSpeed", "calories"]
960
961     #current view
962     labelsC = {}
963
964     #second view ( day / week/ alltime)
965     labelsT = {}
966
967     second_view_labels = ["All-time", "Today", "This week"]
968
969     controller = None
970     pedometer = None
971     pedometerInterval = None
972     graph_controller = None
973
974     mode = 0
975     height = 0
976     unit = 0
977     aspect = 0
978     second_view = 0
979     graph_view = 0
980     no_idle_time = False
981     logging = False
982
983     def __init__(self):
984         hildondesktop.HomePluginItem.__init__(self)
985
986         gobject.type_register(CustomEventBox)
987         gobject.type_register(GraphWidget)
988
989         self.client = gconf.client_get_default()
990         try:
991             self.mode = self.client.get_int(MODE)
992             self.height = self.client.get_int(HEIGHT)
993             self.unit = self.client.get_int(UNIT)
994             self.aspect = self.client.get_int(ASPECT)
995             self.second_view = self.client.get_int(SECONDVIEW)
996             self.graph_view = self.client.get_int(GRAPHVIEW)
997             self.no_idle_time = self.client.get_bool(NOIDLETIME)
998             self.logging = self.client.get_bool(LOGGING)
999
1000         except:
1001             self.client.set_int(MODE, 0)
1002             self.client.set_int(HEIGHT, 0)
1003             self.client.set_int(UNIT, 0)
1004             self.client.set_int(ASPECT, 0)
1005             self.client.set_int(SECONDVIEW, 0)
1006             self.client.set_int(GRAPHVIEW, 0)
1007             self.client.set_bool(NOIDLETIME, False)
1008             self.client.set_bool(LOGGING, False)
1009
1010         self.controller = PedoController()
1011         self.controller.set_height(self.height)
1012         self.controller.set_mode(self.mode)
1013         self.controller.set_unit(self.unit)
1014         self.controller.set_second_view(self.second_view)
1015         self.controller.set_callback_ui(self.update_values)
1016         self.controller.set_no_idle_time(self.no_idle_time)
1017
1018         self.graph_controller = GraphController()
1019         self.graph_controller.set_current_view(self.graph_view)
1020
1021         self.alarm_controller = AlarmController()
1022
1023         self.button = CustomButton(ICONSPATH + "play.png")
1024         self.button.connect("clicked", self.button_clicked)
1025
1026         self.create_labels(self.labelsC)
1027         self.create_labels(self.labelsT)
1028         self.label_second_view = self.new_label_heading(self.second_view_labels[self.second_view])
1029
1030         self.update_current()
1031         self.update_total()
1032
1033         mainHBox = gtk.HBox(spacing=1)
1034
1035         descVBox = gtk.VBox(spacing=1)
1036         descVBox.add(self.new_label_heading())
1037         descVBox.add(self.new_label_heading("Time:"))
1038         descVBox.add(self.new_label_heading("Steps:"))
1039         descVBox.add(self.new_label_heading("Calories:"))
1040         descVBox.add(self.new_label_heading("Distance:"))
1041         descVBox.add(self.new_label_heading("Avg Speed:"))
1042
1043         currentVBox = gtk.VBox(spacing=1)
1044         currentVBox.add(self.new_label_heading("Current"))
1045         currentVBox.add(self.labelsC["timer"])
1046         currentVBox.add(self.labelsC["count"])
1047         currentVBox.add(self.labelsC["calories"])
1048         currentVBox.add(self.labelsC["dist"])
1049         currentVBox.add(self.labelsC["avgSpeed"])
1050         self.currentBox = currentVBox
1051
1052         totalVBox = gtk.VBox(spacing=1)
1053         totalVBox.add(self.label_second_view)
1054         totalVBox.add(self.labelsT["timer"])
1055         totalVBox.add(self.labelsT["count"])
1056         totalVBox.add(self.labelsT["calories"])
1057         totalVBox.add(self.labelsT["dist"])
1058         totalVBox.add(self.labelsT["avgSpeed"])
1059         self.totalBox = totalVBox
1060
1061         buttonVBox = gtk.VBox(spacing=1)
1062         buttonVBox.add(self.new_label_heading(""))
1063         buttonVBox.add(self.button)
1064         buttonVBox.add(self.new_label_heading(""))
1065
1066         eventBox = CustomEventBox()
1067         eventBox.set_visible_window(False)
1068         eventBox.add(totalVBox)
1069         eventBox.connect("button-press-event", self.eventBox_clicked)
1070         eventBox.connect("button-release-event", self.eventBox_clicked_release)
1071
1072         mainHBox.add(buttonVBox)
1073         mainHBox.add(descVBox)
1074         mainHBox.add(currentVBox)
1075         mainHBox.add(eventBox)
1076         self.mainhbox = mainHBox
1077
1078         graph = GraphWidget()
1079         self.graph_controller.set_graph(graph)
1080
1081         eventBoxGraph = CustomEventBox()
1082         eventBoxGraph.set_visible_window(False)
1083         eventBoxGraph.add(graph)
1084         self.graph = graph
1085         eventBoxGraph.connect("button-press-event", self.eventBoxGraph_clicked)
1086         eventBoxGraph.connect("button-release-event", self.eventBoxGraph_clicked_release)
1087
1088         self.mainvbox = gtk.VBox()
1089
1090         self.mainvbox.add(mainHBox)
1091         self.mainvbox.add(eventBoxGraph)
1092
1093         self.mainvbox.show_all()
1094         self.add(self.mainvbox)
1095         self.update_aspect()
1096
1097         self.connect("unrealize", self.close_requested)
1098         self.set_settings(True)
1099         self.connect("show-settings", self.show_settings)
1100
1101     def eventBoxGraph_clicked(self, widget, data=None):
1102         widget.set_state(gtk.STATE_ACTIVE)
1103
1104     def eventBoxGraph_clicked_release(self, widget, data=None):
1105         self.graph_view = self.graph_controller.next_view()
1106         self.client.set_int(GRAPHVIEW, self.graph_view)
1107
1108         widget.set_state(gtk.STATE_NORMAL)
1109
1110     def eventBox_clicked(self, widget, data=None):
1111         widget.set_state(gtk.STATE_ACTIVE)
1112
1113     def eventBox_clicked_release(self, widget, data=None):
1114         widget.set_state(gtk.STATE_NORMAL)
1115
1116         self.second_view = (self.second_view + 1) % 3
1117         self.controller.set_second_view(self.second_view)
1118         self.client.set_int(SECONDVIEW, self.second_view)
1119
1120     def new_label_heading(self, title=""):
1121         l = gtk.Label(title)
1122         hildon.hildon_helper_set_logical_font(l, "SmallSystemFont")
1123         return l
1124
1125     def create_labels(self, new_labels):
1126         for label in self.labels:
1127             l = gtk.Label()
1128             hildon.hildon_helper_set_logical_font(l, "SmallSystemFont")
1129             hildon.hildon_helper_set_logical_color(l, gtk.RC_FG, gtk.STATE_NORMAL, "ActiveTextColor")
1130             new_labels[label] = l
1131
1132     def update_aspect(self):
1133         if self.aspect == 0:
1134             self.currentBox.show_all()
1135             self.totalBox.show_all()
1136         elif self.aspect == 1:
1137             self.currentBox.show_all()
1138             self.totalBox.hide_all()
1139         else:
1140             self.currentBox.hide_all()
1141             self.totalBox.show_all()
1142
1143     def update_ui_values(self, labels, values):
1144         labels["timer"].set_label(values.get_print_time())
1145         labels["count"].set_label(values.get_print_steps())
1146         labels["dist"].set_label(values.get_print_distance())
1147         labels["avgSpeed"].set_label(values.get_print_avg_speed())
1148         labels["calories"].set_label(values.get_print_calories())
1149
1150     def update_current(self):
1151         self.update_ui_values(self.labelsC, self.controller.get_first())
1152
1153     def update_total(self):
1154         self.update_ui_values(self.labelsT, self.controller.get_second())
1155
1156     def show_alarm_settings(self, main_button):
1157         def choose_file(widget):
1158             file = hildon.FileChooserDialog(self, gtk.FILE_CHOOSER_ACTION_OPEN, hildon.FileSystemModel() )
1159             file.show()
1160             if ( file.run() == gtk.RESPONSE_OK):
1161                 fname = file.get_filename()
1162                 widget.set_value(fname)
1163                 self.alarm_controller.set_alarm_file(fname)
1164             file.destroy()
1165
1166         def test_sound(button):
1167             try:
1168                 self.alarm_controller.play()
1169             except Exception, e:
1170                 logger.error("Could not play alarm sound: %s" % e)
1171                 hildon.hildon_banner_show_information(self, "None", "Could not play alarm sound")
1172
1173         def enableButton_changed(button):
1174             value = button.get_active()
1175             self.alarm_controller.set_enable(value)
1176             if value:
1177                 main_button.set_value("Enabled")
1178             else:
1179                 main_button.set_value("Disabled")
1180
1181         def selectorType_changed(selector, data, labelEntry2):
1182             self.alarm_controller.set_type(selector.get_active(0))
1183             labelEntry2.set_label(suffix[self.alarm_controller.get_type()])
1184
1185         dialog = gtk.Dialog()
1186         dialog.set_title("Alarm settings")
1187         dialog.add_button("OK", gtk.RESPONSE_OK)
1188
1189         enableButton = hildon.CheckButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
1190         enableButton.set_label("Enable alarm")
1191         enableButton.set_active(self.alarm_controller.get_enable())
1192         enableButton.connect("toggled", enableButton_changed)
1193
1194         testButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1195         testButton.set_alignment(0, 0.8, 1, 1)
1196         testButton.set_title("Test sound")
1197         testButton.connect("pressed", test_sound)
1198
1199         fileButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1200         fileButton.set_alignment(0, 0.8, 1, 1)
1201         fileButton.set_title("Alarm sound")
1202         fileButton.set_value(self.alarm_controller.get_alarm_file())
1203         fileButton.connect("pressed", choose_file)
1204
1205         labelEntry = gtk.Label("Notify every:")
1206         suffix = ["mins", "steps", "m/ft", "calories"]
1207         labelEntry2 = gtk.Label(suffix[self.alarm_controller.get_type()])
1208         intervalEntry = hildon.Entry(gtk.HILDON_SIZE_AUTO_WIDTH)
1209         intervalEntry.set_text(str(self.alarm_controller.get_interval()))
1210
1211         selectorType = hildon.TouchSelector(text=True)
1212         selectorType.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1213         selectorType.append_text("Time")
1214         selectorType.append_text("Steps")
1215         selectorType.append_text("Distance")
1216         selectorType.append_text("Calories")
1217         selectorType.connect("changed", selectorType_changed, labelEntry2)
1218
1219         typePicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1220         typePicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1221         typePicker.set_title("Alarm type")
1222         typePicker.set_selector(selectorType)
1223         typePicker.set_active(self.alarm_controller.get_type())
1224
1225         hbox = gtk.HBox()
1226         hbox.add(labelEntry)
1227         hbox.add(intervalEntry)
1228         hbox.add(labelEntry2)
1229
1230         dialog.vbox.add(enableButton)
1231         dialog.vbox.add(fileButton)
1232         dialog.vbox.add(typePicker)
1233         dialog.vbox.add(hbox)
1234         dialog.show_all()
1235         while 1:
1236             response = dialog.run()
1237             if response != gtk.RESPONSE_OK:
1238                 break
1239             try:
1240                 value = int(intervalEntry.get_text())
1241                 self.alarm_controller.set_interval(value)
1242                 break
1243             except:
1244                 hildon.hildon_banner_show_information(self, "None", "Invalid interval")
1245
1246         dialog.destroy()
1247
1248     def show_settings(self, widget):
1249         def reset_total_counter(arg):
1250             widget.totalCounter = 0
1251             widget.totalTime = 0
1252             widget.update_total()
1253             hildon.hildon_banner_show_information(self, "None", "Total counter was resetted")
1254
1255         def alarmButton_pressed(widget):
1256             self.show_alarm_settings(widget)
1257
1258         def selector_changed(selector, data):
1259             widget.mode = selector.get_active(0)
1260             widget.client.set_int(MODE, widget.mode)
1261             widget.controller.set_mode(widget.mode)
1262
1263         def selectorH_changed(selector, data):
1264             widget.height = selectorH.get_active(0)
1265             widget.client.set_int(HEIGHT, widget.height)
1266             widget.controller.set_height(widget.height)
1267
1268         def selectorUnit_changed(selector, data):
1269             widget.unit = selectorUnit.get_active(0)
1270             widget.client.set_int(UNIT, widget.unit)
1271             widget.controller.set_unit(widget.unit)
1272
1273         def selectorUI_changed(selector, data):
1274             widget.aspect = selectorUI.get_active(0)
1275             widget.client.set_int(ASPECT, widget.aspect)
1276             widget.update_aspect()
1277
1278         def logButton_changed(checkButton):
1279             widget.logging = checkButton.get_active()
1280             widget.client.set_bool(LOGGING, widget.logging)
1281
1282         def idleButton_changed(idleButton):
1283             widget.no_idle_time = idleButton.get_active()
1284             widget.client.set_bool(NOIDLETIME, widget.no_idle_time)
1285             widget.controller.set_no_idle_time(widget.no_idle_time)
1286
1287         dialog = gtk.Dialog()
1288         dialog.set_title("Settings")
1289         dialog.add_button("OK", gtk.RESPONSE_OK)
1290
1291
1292         button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1293         button.set_title("Reset total counter")
1294         button.set_alignment(0, 0.8, 1, 1)
1295         button.connect("clicked", reset_total_counter)
1296
1297         alarmButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1298         alarmButton.set_title("Alarm")
1299         if self.alarm_controller.get_enable():
1300             alarmButton.set_value("Enabled")
1301         else:
1302             alarmButton.set_value("Disabled")
1303         alarmButton.set_alignment(0, 0.8, 1, 1)
1304         alarmButton.connect("clicked", alarmButton_pressed)
1305
1306         selector = hildon.TouchSelector(text=True)
1307         selector.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1308         selector.append_text("Walk")
1309         selector.append_text("Run")
1310         selector.connect("changed", selector_changed)
1311
1312         modePicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1313         modePicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1314         modePicker.set_title("Select mode")
1315         modePicker.set_selector(selector)
1316         modePicker.set_active(widget.mode)
1317
1318         selectorH = hildon.TouchSelector(text=True)
1319         selectorH.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1320         selectorH.append_text("< 1.50 m")
1321         selectorH.append_text("1.50 - 1.65 m")
1322         selectorH.append_text("1.66 - 1.80 m")
1323         selectorH.append_text("1.81 - 1.95 m")
1324         selectorH.append_text(" > 1.95 m")
1325         selectorH.connect("changed", selectorH_changed)
1326
1327         heightPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1328         heightPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1329         heightPicker.set_title("Select height")
1330         heightPicker.set_selector(selectorH)
1331         heightPicker.set_active(widget.height)
1332
1333         selectorUnit = hildon.TouchSelector(text=True)
1334         selectorUnit.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1335         selectorUnit.append_text("Metric (km)")
1336         selectorUnit.append_text("English (mi)")
1337         selectorUnit.connect("changed", selectorUnit_changed)
1338
1339         unitPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1340         unitPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1341         unitPicker.set_title("Units")
1342         unitPicker.set_selector(selectorUnit)
1343         unitPicker.set_active(widget.unit)
1344
1345         selectorUI = hildon.TouchSelector(text=True)
1346         selectorUI = hildon.TouchSelector(text=True)
1347         selectorUI.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_SINGLE)
1348         selectorUI.append_text("Show current + total")
1349         selectorUI.append_text("Show only current")
1350         selectorUI.append_text("Show only total")
1351         selectorUI.connect("changed", selectorUI_changed)
1352
1353         UIPicker = hildon.PickerButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
1354         UIPicker.set_alignment(0.0, 0.5, 1.0, 1.0)
1355         UIPicker.set_title("Widget aspect")
1356         UIPicker.set_selector(selectorUI)
1357         UIPicker.set_active(widget.aspect)
1358
1359         logButton = hildon.CheckButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
1360         logButton.set_label("Log data")
1361         logButton.set_active(widget.logging)
1362         logButton.connect("toggled", logButton_changed)
1363
1364         idleButton = hildon.CheckButton(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT)
1365         idleButton.set_label("Pause time when not walking")
1366         idleButton.set_active(widget.no_idle_time)
1367         idleButton.connect("toggled", idleButton_changed)
1368
1369         pan_area = hildon.PannableArea()
1370         vbox = gtk.VBox()
1371         vbox.add(button)
1372         vbox.add(alarmButton)
1373         vbox.add(modePicker)
1374         vbox.add(heightPicker)
1375         vbox.add(unitPicker)
1376         vbox.add(UIPicker)
1377         vbox.add(idleButton)
1378         #vbox.add(logButton)
1379
1380         pan_area.add_with_viewport(vbox)
1381         pan_area.set_size_request(-1, 300)
1382
1383         dialog.vbox.add(pan_area)
1384         dialog.show_all()
1385         response = dialog.run()
1386         #hildon.hildon_banner_show_information(self, "None", "You have to Stop/Start the counter to apply the new settings")
1387         dialog.destroy()
1388
1389     def close_requested(self, widget):
1390         if self.pedometer is None:
1391             return
1392
1393         self.pedometer.request_stop()
1394
1395     def update_values(self):
1396         #TODO: do not update if the widget is not on the active desktop
1397         self.label_second_view.set_label(self.second_view_labels[self.second_view])
1398         self.update_current()
1399         self.update_total()
1400
1401     def button_clicked(self, button):
1402         if self.controller.is_running:
1403             self.controller.stop_pedometer()
1404             self.button.set_icon(ICONSPATH + "play.png")
1405         else:
1406             self.controller.start_pedometer()
1407             self.button.set_icon(ICONSPATH + "stop.png")
1408
1409     def do_expose_event(self, event):
1410         cr = self.window.cairo_create()
1411         cr.region(event.window.get_clip_region())
1412         cr.clip()
1413         #cr.set_source_rgba(0.4, 0.64, 0.564, 0.5)
1414         style = self.rc_get_style()
1415         color = style.lookup_color("DefaultBackgroundColor")
1416         cr.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.75);
1417
1418         radius = 5
1419         width = self.allocation.width
1420         height = self.allocation.height
1421
1422         x = self.allocation.x
1423         y = self.allocation.y
1424
1425         cr.move_to(x + radius, y)
1426         cr.line_to(x + width - radius, y)
1427         cr.curve_to(x + width - radius, y, x + width, y, x + width, y + radius)
1428         cr.line_to(x + width, y + height - radius)
1429         cr.curve_to(x + width, y + height - radius, x + width, y + height, x + width - radius, y + height)
1430         cr.line_to(x + radius, y + height)
1431         cr.curve_to(x + radius, y + height, x, y + height, x, y + height - radius)
1432         cr.line_to(x, y + radius)
1433         cr.curve_to(x, y + radius, x, y, x + radius, y)
1434
1435         cr.set_operator(cairo.OPERATOR_SOURCE)
1436         cr.fill_preserve()
1437
1438         color = style.lookup_color("ActiveTextColor")
1439         cr.set_source_rgba (color.red / 65535.0, color.green / 65335.0, color.blue / 65535.0, 0.5);
1440         cr.set_line_width(1)
1441         cr.stroke()
1442
1443         hildondesktop.HomePluginItem.do_expose_event(self, event)
1444
1445     def do_realize(self):
1446         screen = self.get_screen()
1447         self.set_colormap(screen.get_rgba_colormap())
1448         self.set_app_paintable(True)
1449         hildondesktop.HomePluginItem.do_realize(self)
1450
1451 hd_plugin_type = PedometerHomePlugin
1452
1453 # The code below is just for testing purposes.
1454 # It allows to run the widget as a standalone process.
1455 if __name__ == "__main__":
1456     import gobject
1457     gobject.type_register(hd_plugin_type)
1458     obj = gobject.new(hd_plugin_type, plugin_id="plugin_id")
1459     obj.show_all()
1460     gtk.main()
1461
1462 ############### old pedometer.py ###
1463 import math
1464 import logging
1465
1466 logger = logging.getLogger("pedometer")
1467 logger.setLevel(logging.INFO)
1468
1469 ch = logging.StreamHandler()
1470 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
1471 ch.setFormatter(formatter)
1472 logger.addHandler(ch)
1473