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