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