Imported Upstream version 0.0.4
[callnotify] / src / usr / lib / hildon-desktop / CallNotify.py
1 import gtk
2 import gobject
3 import hildon, hildondesktop
4 import sqlite3
5 import time
6 import dbus
7 import osso
8 import atexit, os, datetime
9 from dbus.mainloop.glib import DBusGMainLoop
10
11
12 class CallNotify(hildondesktop.StatusMenuItem):
13     def __init__(self):
14                 hildondesktop.StatusMenuItem.__init__(self)
15                 # Set members
16                 self.Debug = False
17                 self.configDir = "/home/user/.config/CallNotify/"
18                 self.configFile = "conf.txt"
19                 self.readConfigurationFile()
20                 self.msgType = ""               
21                 self.toShow = True
22                 self.stop = False
23                 self.path = "/home/user/.rtcom-eventlogger/el.db"
24                 self.missed = self.getMissedCallsCount(False)
25                 self.missedSMS = self.getMissedCallsCount(True)
26                 self.missedLastCall = self.missed
27                 self.missedLastSMS = self.missedSMS
28                 self.mainLoop = None    
29                 self.soundFile = "/usr/share/CallNotify/missed.wav"
30                 self.dbg('constructor')
31                 
32                 # Load images
33                 self.loadImages()
34                         
35                 # Register to handle screen off/on events
36                 osso_c = osso.Context("osso_test_device_on", "0.0.1", False)
37                 device = osso.DeviceState(osso_c)
38                 #device.set_display_event_cb(self.state_cb)
39                 
40                 # Check missed calls notification
41                 self.tmr_main = gobject.timeout_add(5000, self.handleMissedCall) 
42                 self.tmrset = True      
43                 # add d-bus listener for removing notification after viewing missed call
44                 # Doing timeout_add with return False instead of explicitly raising a thread
45                 gobject.timeout_add(500, self.startDbusListeners)
46                 #atexit.register(self.cleanup)
47                 
48                 # add GUI buttons
49                 self.addGUI()
50                 dbg('constructor end')
51         
52     def addGUI(self):
53           # add GUI buttons                                              
54                 label = gtk.Label("Call Notify")                               
55                 button = gtk.Button()                                          
56                 button.add(label)                                              
57                 button.connect("clicked", self.openSettingsDialog)             
58                 self.add(button)                                               
59                 self.show_all()    
60                 self.dbg('addGUI end')
61
62     def checkForConfigFile(self):
63                 self.dbg('checkForConfigFile started')
64                 if not os.path.exists(self.configDir):
65                         os.mkdir(self.configDir)
66                 if not os.path.exists(self.configDir+self.configFile):
67                         a = open(self.configDir+self.configFile,'w')
68                         a.write('y;y;y;5')
69                         a.close()
70
71                 
72     def readConfigurationFile(self):
73                 self.dbg('readConfigurationFile started')
74                 self.checkForConfigFile()
75                 f = open(self.configDir+self.configFile, 'r')
76                 raw_set = f.readline().rsplit(';')
77                 self.visual = raw_set[0] in ('y')
78                 self.sound = raw_set[1] in ('y')
79                 self.vibration = raw_set[2] in ('y')
80                 self.interval = float(raw_set[3])
81                 f.close()
82                         
83     def saveConfigurationFile(self):
84                 self.dbg('saveConfigurationFile started')
85                 f = open(self.configDir+self.configFile, "w")
86                 conf = ''
87                 if self.visual:
88                         conf += 'y;'
89                 else:
90                         conf += 'n;'
91
92                 if self.sound:
93                         conf += 'y;'
94                 else:
95                         conf +='n;'
96         
97                 if self.vibration:
98                         conf += 'y;'
99                 else:
100                         conf += 'n;'
101
102                 conf += str(self.interval)
103         
104                 f.write(conf)
105                 f.close()
106
107     def openSettingsDialog(self, widget, data=None):
108                 self.dbg('openSettingsDialog started')
109                 self.dialog = gtk.Dialog(title="Call Notify Settings")               
110                 self.dialog.set_size_request(800,300)
111                 #self.dialog.connect("response", self.dialogClosed)  
112                 self.readConfigurationFile()            
113                 # Visual
114                                 
115                 b2 = gtk.CheckButton(label="Visual Notification On")           
116                 b2.connect("clicked", self.notificationActivate)               
117                 b2.set_active(self.visual)
118                 self.dialog.vbox.add(b2)                    
119                                 
120                                 # Sound
121                                 
122                 b3 = gtk.CheckButton(label="Sound Notification On")            
123                 b3.connect("clicked", self.soundActivate)                      
124                 b3.set_active(self.sound) 
125                 self.dialog.vbox.add(b3)  
126                                 
127                                 # Vibration
128                                 
129                 b4 = gtk.CheckButton(label="Vibrate Notification On")            
130                 b4.connect("clicked", self.vibrateActivate)                      
131                 b4.set_active(self.vibration)
132                 self.dialog.vbox.add(b4)  
133                                 
134                                 # Slider
135                                 
136                 Adj = gtk.Adjustment(self.interval, lower=0, upper=60, step_incr=5, page_incr=5)
137                 Adj.connect("value_changed", self.intervalChanged)
138                 Adj.set_value(self.interval)
139                                 
140                 Slider = gtk.HScale(adjustment=Adj)
141                 self.dialog.vbox.add(Slider)
142                                 
143                                 # Manual reset
144                                 
145                 b5 = gtk.Button(label="Manually reset notification")           
146                 b5.connect("clicked", self.resetNotification)                  
147                 self.dialog.vbox.add(b5)
148                 
149                                 # Save Button
150                 
151                 bSave = gtk.Button(label="Save")
152                 bSave.connect("clicked", self.saveSettings)
153                 self.dialog.action_area.add(bSave)
154                                 
155                                 # Cancel Button
156                                 
157                 bCancel = gtk.Button(label="Cancel")
158                 bCancel.connect("clicked", self.cancelDialog)
159                 self.dialog.vbox.add(bCancel)
160                 
161                 self.dialog.show_all()
162
163     def intervalChanged(self, adj):
164                 self.dbg('intervalChanged started')
165                 self.interval = adj.value
166
167     def saveSettings(self, widget, data=None):
168                 self.dbg('saveSettings started')
169                 self.saveConfigurationFile()
170         
171     def dialogClosed(self, dialog, response_id):
172                 self.dbg('dialogClosed started')
173         
174     def cancelDialog(self, widget, data=None):
175                 self.dbg('cancelDialog started')
176                 self.dialog.destroy()
177         
178     def resetNotification(self, widget, data=None):
179                 self.dbg('resetNotification started')
180                 self.stop_notification(self)
181
182     def soundActivate(self, widget, data=None):
183                 self.dbg('soundActivate started')
184                 self.sound = widget.get_active() #not(self.sound)
185         
186     def notificationActivate(self,widget, data=None):
187                 self.dbg('notificationActivate started')
188                 self.visual = widget.get_active() #not(self.visual)
189
190     def vibrateActivate(self, widget, data=None):
191                 self.dbg('vibrateActivate started')
192                 self.vibration = widget.get_active() #not(self.vibrate)
193
194         
195     def playSound(self):
196                 self.dbg('playSound started')
197                 if self.sound:
198                         hildon.hildon_play_system_sound(self.soundFile)
199                         #pygame.time.delay(1000)
200                 if self.vibration:
201                         bb = 'run-standalone.sh dbus-send --print-reply --system --dest=com.nokia.mce /com/nokia/mce/request com.nokia.mce.request.req_vibrator_pattern_activate string:' + "\'PatternIncomingCall\'"
202                         bb = str(bb)
203                         b = os.popen(bb)
204                         b.close()
205                         bb = 'run-standalone.sh dbus-send --print-reply --system --dest=com.nokia.mce /com/nokia/mce/request com.nokia.mce.request.req_vibrator_pattern_deactivate string:' + "\'PatternIncomingCall\'" 
206                         b = os.popen(bb)
207                         b.close()
208                 return True
209                 
210         
211     def cleanup(self):
212                 self.dbg('cleanup started')
213                 gobject.source_remove(self.tmr_main)                  
214                 gobject.source_remove(self.tmr_ptr)     
215                 gobject.source_remove(self.tmr_ptr2)
216                 
217                 self.mainLoop.quit()
218
219     def loadImages(self):
220                 self.dbg('loadImages started')
221                 # Load phone image
222                 #self.pixbuf = gtk.gdk.pixbuf_new_from_file_at_size("/home/user/phone.png",18,18)
223                 icon_theme = gtk.icon_theme_get_default()
224                 self.callPicture = gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/call.png",18,18)
225                 #icon_theme.load_icon("general_call", 18, gtk.ICON_LOOKUP_NO_SVG)
226                 self.smsPicture = gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/sms.png",18,18)
227                 
228                 # Load 5 numbers and the "+5" 
229                 self.imgList = []
230                 #self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/home/user/1.png",18,18))
231                 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/1.png",18,18))
232                 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/2.png",18,18))
233                 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/3.png",18,18))
234                 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/4.png",18,18))
235                 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/5.png",18,18))
236                 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/more.png",18,18))
237                 
238         # Screen off event-handler
239     def state_cb(self, state, a):
240                 self.dbg('state_cb started')
241                 if state == osso.device_state.OSSO_DISPLAY_OFF:
242                         try:
243                                 #gobject.source_remove(self.tmr_main)
244                                 self.tmrset = False
245                                 #gobject.source_remove(self.tmr_ptr)
246                                 
247                                 #gobject.source_remove(self.tmr_ptr2)
248                         except:
249                                 pass
250                 elif state == osso.device_state.OSSO_DISPLAY_ON:
251                         if not tmrset:
252                                 pass
253                                 #self.tmr_main = gobject.timeout_add(5000, self.handleMissedCall)
254                         #self.handleMissedCall()
255                 return False
256                 
257         # Method to define the way to add dbus signal receiver
258
259     def smsrec(self):
260                 self.dbg('smsrec started')
261                 self.stop_notification(self)
262
263     def startDbusListeners(self):
264                 self.dbg('startDbusListeners started')
265                 DBusGMainLoop(set_as_default=True)                             
266                 bus = dbus.SessionBus()                                        
267                 #bus.add_signal_receiver(self.stop_notification, "NotificationClosed", "org.freedesktop.Notifications", "org.freedesktop.Notifications", "/org/freedesktop/Notifications") 
268                 #bus.add_signal_receiver(self.handleMissedCall, "Notify", None, None, None)
269                 #bus.add_signal_receiver(self.handleMissedCall, "MembersChanged", None, None, None)
270                 bus.add_signal_receiver(self.smsReceived, "MessageReceived", None, None, None)
271                 bus.add_signal_receiver(self.smsRead, "NotificationClosed", "org.freedesktop.Notifications", None, "/org/freedesktop/Notifications")
272                 bus.add_signal_receiver(self.smsrec, "Closed", None, None, None)
273
274                 self.mainLoop = gobject.MainLoop()
275                 self.mainLoop.run()                                       
276                 return False
277     
278     def smsReceived(self, a):
279                 self.dbg('snsReceived started')
280                 if a[0].has_key('message-type'):
281                         if self.missedLastSMS == self.getMissedCallsCount(True):
282                                 if self.msgType == "Call":
283                                         self.msgType = "Both"
284                                 else:
285                                         self.msgType = "SMS"
286                                 self.show()
287                         self.missedLastSMS = self.getMissedCallsCount(True)
288         
289     def smsRead(self, a):
290                 self.dbg('smsRead started')
291                 self.stop_notification(a)
292         
293     def handleMissedCall(self): 
294                 self.dbg('handleMissedCall started')
295                 if self.missedLastCall != self.getMissedCallsCount(False):
296                         if self.msgType == "SMS":
297                                 self.msgType = "Both"
298                         else:
299                                 self.msgType = "Call"
300                         self.show()
301                         self.missedLastCall = self.getMissedCallsCount(False)
302                 return True
303         
304     def stop_notification(self, a):
305                 self.dbg('stop_notification started')
306                 try:
307                         self.set_status_area_icon(None)
308                         # Reset the notification (get recent missed call count)
309                         self.missed = self.getMissedCallsCount(False)
310                         self.missedSMS = self.getMissedCallsCount(True)
311                         self.missedLastCall = self.missed
312                         self.missedLastSMS = self.missedSMS
313                         self.stop = False
314                         self.msgType = ""
315                         gobject.source_remove(self.tmr_ptr)
316                         gobject.source_remove(self.tmr_ptr2)
317                 except:
318                         pass
319
320
321     def theLoop(self):
322                 self.dbg('theLoop started')
323                 missedCalls = self.getMissedCallsCount(False)
324                 if self.missedLastCall != missedCalls:
325                         self.show()
326                         self.missedLastCall  = missedCalls
327                 return True
328
329     def getMissedCallsCount(self, isSms):
330                 self.dbg('getMissedCallsCount started. agrs: ' + str(isSms))
331                 eType = 3
332                 if isSms:
333                         eType=7
334                 conn = sqlite3.connect(self.path)
335                 cur = conn.cursor()
336                 cur.execute("select count(id) from Events where event_type_id = " + str(eType))
337                 return cur.fetchone()[0]
338
339     def show(self):
340                 self.dbg('show started')
341                 # blink the icon every 1 second
342                 if not(self.stop) and self.visual:
343                         self.stop = True
344                         self.tmr_ptr = gobject.timeout_add(1000, self.blinkIcon)
345                         self.tmr_ptr2 = gobject.timeout_add(int(self.interval*1000*60), self.playSound)
346                         
347     def blinkIcon(self):
348                 self.dbg('blinkIcon started')
349                 if self.toShow:
350                         self.toShow = False
351                         img = self.callPicture
352                         if self.msgType == "SMS":
353                                 img = self.smsPicture
354                         self.set_status_area_icon(img)
355                         return True
356                 else:
357                         img = self.smsPicture
358                         isSMS = False
359                         counter = self.missed
360                         if self.msgType == "SMS":
361                                 counter = self.missedSMS
362                                 isSMS = True
363                         index = self.getMissedCallsCount(isSMS) - counter - 1
364                         if index >= 5:
365                                 index = 5
366                                 if index < 0:
367                                         index = 0
368                         if self.msgType != "Both":
369                                 img = self.imgList[index]
370                         self.toShow = True
371                         self.set_status_area_icon(img)
372                         return True
373                         
374     def dbg(self, txt):
375                         if self.Debug:
376                                 f = open(self.configDir+'log.txt', 'a')
377                                 f.write(str(datetime.datetime.now()) + ': '+ txt)
378                                 f.write('\n')
379
380                                 f.close()
381                 
382 hd_plugin_type = CallNotify
383