Import patched yrannadx version from http://talk.maemo.org/showpost.php?p=1164866...
[callnotify] / src / usr / lib / hildon-desktop / CallNotify.py
old mode 100755 (executable)
new mode 100644 (file)
index 4552a8b..6f81c18
@@ -1,4 +1,4 @@
-import gtk
+import gtk, gst
 import gobject
 import hildon, hildondesktop
 import sqlite3
@@ -8,73 +8,112 @@ import osso
 import atexit, os, datetime
 from dbus.mainloop.glib import DBusGMainLoop
 
+WriteLog = False
 
+class Playbin2:
+       def __init__(self):
+               self.idle = True # not playing at the moment
+               self.configDir = "/home/user/.config/CallNotify/"
+               self.Debug = WriteLog
+               # create a playbin2 pipe
+               self.player = gst.element_factory_make("playbin2", "player")
+               # connect a signal handler to it's bus
+               bus = self.player.get_bus()
+               bus.add_signal_watch()
+               bus.connect("message", self.on_message)
+
+       def on_message(self, bus, message):
+               t = message.type
+               if t == gst.MESSAGE_EOS:
+                       self.player.set_state(gst.STATE_NULL)
+                       self.idle = True
+                       self.dbg('Playbin2: EOS: STATE_NULL')
+               elif t == gst.MESSAGE_ERROR:
+                       #err, debug = message.parse_error()
+                       #print >> sys.stderr, "Error: {0} {1}".format(err, debug)
+                       self.player.set_state(gst.STATE_NULL)
+                       self.idle = True
+                       self.dbg('Playbin2: ERROR: STATE_NULL')
+               return self.idle
+
+       def play(self, file, volume):
+               # abort previous play if still busy
+               if not self.idle:
+                       #print >> sys.stderr, 'audio truncated'
+                       self.player.set_state(gst.STATE_NULL)
+               self.player.set_property("uri", "file://" + file)
+               if volume > 0.0:
+                       self.player.set_property("volume", min(volume, 1.0))
+               self.dbg('Volume:' + str(self.player.get_property("volume")))
+               self.player.set_state(gst.STATE_PLAYING)
+               self.idle = False # now playing
+
+       def dbg(self, txt):
+                       if self.Debug:
+                               f = open(self.configDir+'log.txt', 'a')
+                               f.write(str(datetime.datetime.now()) + ': '+ txt)
+                               f.write('\n')
+
+                               f.close()
+               
 class CallNotify(hildondesktop.StatusMenuItem):
-    def __init__(self):
+       def __init__(self):
                hildondesktop.StatusMenuItem.__init__(self)
                # Set members
                self.configDir = "/home/user/.config/CallNotify/"
                self.configFile = "conf.txt"
-               self.Debug = False
+               self.configSoundFile = "sound.txt"
+               self.Debug = WriteLog
                self.dbg("debugging started")
-               self.readConfigurationFile()
-               self.msgType = ""               
+               self.msgType = ""
                self.toShow = True
                self.stop = False
-               self.path = "/home/user/.rtcom-eventlogger/el-v1.db"
-               self.missed = self.getMissedCallsCount(False)
-               self.missedSMS = self.getMissedCallsCount(True)
-               self.missedLastCall = self.missed
-               self.missedLastSMS = self.missedSMS
-               self.mainLoop = None    
+               self.path = "/home/user/.config/hildon-desktop/notifications.db"
+               self.mainLoop = None
                self.soundFile = "/usr/share/CallNotify/missed.wav"
+               self.soundCall = self.soundFile
+               self.soundSMS = self.soundFile
+               self.soundBoth = self.soundFile
+               self.volume = 0.0
+               self.readConfigurationFile()
+
                self.dbg('constructor')
-               
+
                # Load images
                self.loadImages()
-                       
+               
                # Register to handle screen off/on events
                osso_c = osso.Context("osso_test_device_on", "0.0.1", False)
                device = osso.DeviceState(osso_c)
-               #device.set_display_event_cb(self.state_cb)
                
-               # Check missed calls notification
-               self.tmr_main = gobject.timeout_add(5000, self.handleMissedCall) 
-               self.tmrset = True      
                # add d-bus listener for removing notification after viewing missed call
                # Doing timeout_add with return False instead of explicitly raising a thread
                gobject.timeout_add(500, self.startDbusListeners)
-               #atexit.register(self.cleanup)
                
-               # add GUI buttons
-               # self.addGUI()
+               if self.Debug:
+                       hildon.hildon_play_system_sound(self.soundFile)
                self.dbg('constructor end')
        
-    def addGUI(self):
-         # add GUI buttons                                              
-                label = gtk.Label("Call Notify")                               
-                button = gtk.Button()                                          
-                button.add(label)                                              
-                button.connect("clicked", self.openSettingsDialog)             
-                self.add(button)                                               
-                self.show_all()    
-               self.dbg('addGUI end')
-
-    def checkForConfigFile(self):
+       def checkForConfigFile(self):
                self.dbg('checkForConfigFile started')
                os.umask(0)
                if not os.path.exists(self.configDir):
-                       os.mkdir(self.configDir)
+                               os.mkdir(self.configDir)
                if not os.path.exists(self.configDir+self.configFile):
-                       a = open(self.configDir+self.configFile,'w+')
-                       a.write('y;y;y;5.0\n')
-                       a.close()
+                               a = open(self.configDir+self.configFile,'w+')
+                               a.write('y;y;y;5.0\n')
+                               a.close()
+               if not os.path.exists(self.configDir+self.configSoundFile):
+                               a = open(self.configDir+self.configSoundFile,'w+')
+                               a.write('\n')
+                               a.close()
                # set proper permissions
                os.system("chmod 766 " + self.configDir)
                os.system("chmod 766" + self.configDir+self.configFile)
+               os.system("chmod 766" + self.configDir+self.configSoundFile)
 
-                
-    def readConfigurationFile(self):
+
+       def readConfigurationFile(self):
                try:
                        self.dbg('readConfigurationFile started')
                        self.checkForConfigFile()
@@ -85,127 +124,44 @@ class CallNotify(hildondesktop.StatusMenuItem):
                        self.vibration = raw_set[2] in ('y')
                        self.interval = float(raw_set[3].replace(',','.'))
                        f.close()
+                       
+                       # read sound config file
+                       f = open(self.configDir+self.configSoundFile, 'r')
+                       line = f.readline()
+                       # check if specific missed call, SMS or common sound was defined in config file
+                       if line:
+                               self.soundCall = line.rstrip()
+                       line = f.readline()
+                       if line:
+                               self.soundSMS = line.rstrip()
+                       line = f.readline()
+                       if line:
+                               self.soundBoth = line.rstrip()
+                       line = f.readline()
+                       if line:
+                               self.volume = float(line.rstrip())
+                       f.close()
                except:
                        os.remove(self.configDir+self.configFile)
+                       os.remove(self.configDir+self.configSoundFile)
                        self.checkForConfigFile()
        
-    def saveConfigurationFile(self):
-               self.dbg('saveConfigurationFile started')
-               f = open(self.configDir+self.configFile, "w")
-               conf = ''
-               if self.visual:
-                       conf += 'y;'
-               else:
-                       conf += 'n;'
-
-               if self.sound:
-                       conf += 'y;'
-               else:
-                       conf +='n;'
-       
-               if self.vibration:
-                       conf += 'y;'
-               else:
-                       conf += 'n;'
-
-               conf += str(self.interval)
-       
-               f.write(conf)
-               f.close()
-
-    def openSettingsDialog(self, widget, data=None):
-               self.dbg('openSettingsDialog started')
-               self.dialog = gtk.Dialog(title="Call Notify Settings")               
-                self.dialog.set_size_request(800,300)
-               #self.dialog.connect("response", self.dialogClosed)  
-               self.readConfigurationFile()            
-               # Visual
-                               
-                b2 = gtk.CheckButton(label="Visual Notification On")           
-                b2.connect("clicked", self.notificationActivate)               
-               b2.set_active(self.visual)
-                self.dialog.vbox.add(b2)                    
-                               
-                               # Sound
-                               
-                b3 = gtk.CheckButton(label="Sound Notification On")            
-                b3.connect("clicked", self.soundActivate)                      
-               b3.set_active(self.sound) 
-                self.dialog.vbox.add(b3)  
-                               
-                               # Vibration
-                               
-               b4 = gtk.CheckButton(label="Vibrate Notification On")            
-                b4.connect("clicked", self.vibrateActivate)                      
-               b4.set_active(self.vibration)
-                self.dialog.vbox.add(b4)  
-                               
-                               # Slider
-                               
-               Adj = gtk.Adjustment(self.interval, lower=0, upper=60, step_incr=5, page_incr=5)
-               Adj.connect("value_changed", self.intervalChanged)
-               Adj.set_value(self.interval)
-                               
-               Slider = gtk.HScale(adjustment=Adj)
-               self.dialog.vbox.add(Slider)
-                               
-                               # Manual reset
-                               
-               b5 = gtk.Button(label="Manually reset notification")           
-                b5.connect("clicked", self.resetNotification)                  
-               self.dialog.vbox.add(b5)
-               
-                               # Save Button
-               
-               bSave = gtk.Button(label="Save")
-               bSave.connect("clicked", self.saveSettings)
-               self.dialog.action_area.add(bSave)
-                               
-                               # Cancel Button
-                               
-               bCancel = gtk.Button(label="Cancel")
-               bCancel.connect("clicked", self.cancelDialog)
-               self.dialog.vbox.add(bCancel)
-               
-               self.dialog.show_all()
-
-    def intervalChanged(self, adj):
-               self.dbg('intervalChanged started')
-               self.interval = adj.value
-
-    def saveSettings(self, widget, data=None):
-               self.dbg('saveSettings started')
-               self.saveConfigurationFile()
-       
-    def dialogClosed(self, dialog, response_id):
-               self.dbg('dialogClosed started')
-       
-    def cancelDialog(self, widget, data=None):
-               self.dbg('cancelDialog started')
-               self.dialog.destroy()
-       
-    def resetNotification(self, widget, data=None):
-               self.dbg('resetNotification started')
-               self.stop_notification(self)
-
-    def soundActivate(self, widget, data=None):
-               self.dbg('soundActivate started')
-               self.sound = widget.get_active() #not(self.sound)
-       
-    def notificationActivate(self,widget, data=None):
-               self.dbg('notificationActivate started')
-               self.visual = widget.get_active() #not(self.visual)
-
-    def vibrateActivate(self, widget, data=None):
-               self.dbg('vibrateActivate started')
-               self.vibration = widget.get_active() #not(self.vibrate)
-
-       
-    def playSound(self):
+       def playSound(self):
                self.dbg('playSound started')
                if self.sound:
-                       hildon.hildon_play_system_sound(self.soundFile)
-                       #pygame.time.delay(1000)
+                       # Create the player_name sink
+                       if self.msgType == "Call":
+                               self.dbg('play soundCall:' + self.soundCall)
+                               Playbin2().play(self.soundCall, self.volume)
+                       elif self.msgType == "SMS":
+                               self.dbg('play soundSMS:' + self.soundSMS)
+                               Playbin2().play(self.soundSMS, self.volume)
+                       elif self.msgType == "Both":
+                               self.dbg('play soundBoth:' + self.soundBoth)
+                               Playbin2().play(self.soundBoth, self.volume)
+                       else:
+                               Playbin2().play(self.soundFile, self.volume)
+
                if self.vibration:
                        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\'"
                        bb = str(bb)
@@ -216,27 +172,14 @@ class CallNotify(hildondesktop.StatusMenuItem):
                        b.close()
                return True
                
-       
-    def cleanup(self):
-               self.dbg('cleanup started')
-               gobject.source_remove(self.tmr_main)                  
-               gobject.source_remove(self.tmr_ptr)     
-               gobject.source_remove(self.tmr_ptr2)
-               
-               self.mainLoop.quit()
-
-    def loadImages(self):
+       def loadImages(self):
                self.dbg('loadImages started')
-               # Load phone image
-               #self.pixbuf = gtk.gdk.pixbuf_new_from_file_at_size("/home/user/phone.png",18,18)
                icon_theme = gtk.icon_theme_get_default()
                self.callPicture = gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/call.png",18,18)
-               #icon_theme.load_icon("general_call", 18, gtk.ICON_LOOKUP_NO_SVG)
                self.smsPicture = gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/sms.png",18,18)
                
                # Load 5 numbers and the "+5" 
                self.imgList = []
-               #self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/home/user/1.png",18,18))
                self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/1.png",18,18))
                self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/2.png",18,18))
                self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/3.png",18,18))
@@ -244,112 +187,85 @@ class CallNotify(hildondesktop.StatusMenuItem):
                self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/5.png",18,18))
                self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/more.png",18,18))
                
-       # Screen off event-handler
-    def state_cb(self, state, a):
-               self.dbg('state_cb started')
-               if state == osso.device_state.OSSO_DISPLAY_OFF:
-                       try:
-                               #gobject.source_remove(self.tmr_main)
-                               self.tmrset = False
-                               #gobject.source_remove(self.tmr_ptr)
-                               
-                               #gobject.source_remove(self.tmr_ptr2)
-                       except:
-                               pass
-               elif state == osso.device_state.OSSO_DISPLAY_ON:
-                       if not tmrset:
-                               pass
-                               #self.tmr_main = gobject.timeout_add(5000, self.handleMissedCall)
-                       #self.handleMissedCall()
-               return False
-               
-       # Method to define the way to add dbus signal receiver
-
-    def smsRead2(self, a):
-               self.dbg('smsrec started')
-               self.stop_notification(self)
-
-    def startDbusListeners(self):
+       def startDbusListeners(self):
                self.dbg('startDbusListeners started')
-               DBusGMainLoop(set_as_default=True)                             
-                bus = dbus.SessionBus()                                        
-                #bus.add_signal_receiver(self.stop_notification, "NotificationClosed", "org.freedesktop.Notifications", "org.freedesktop.Notifications", "/org/freedesktop/Notifications") 
-               #bus.add_signal_receiver(self.handleMissedCall, "Notify", None, None, None)
-               #bus.add_signal_receiver(self.handleMissedCall, "MembersChanged", None, None, None)
-               bus.add_signal_receiver(self.smsReceived, "MessageReceived", None, None, None)
-               bus.add_signal_receiver(self.smsRead, "NotificationClosed", "org.freedesktop.Notifications", None, "/org/freedesktop/Notifications")
-               bus.add_signal_receiver(self.smsRead2, "PendingMessagesRemoved", None, None, None)
+               DBusGMainLoop(set_as_default=True)
+               bus = dbus.SessionBus()
 
-                self.mainLoop = gobject.MainLoop()
-               self.mainLoop.run()                                       
+               bus.add_signal_receiver(self.notificationClosed, "NotificationClosed", "org.freedesktop.Notifications", None, "/org/freedesktop/Notifications")
+               bus.add_signal_receiver(self.pendingMessagesRemoved, "PendingMessagesRemoved", None, None, None)
+               bus.add_signal_receiver(self.newEvent, "NewEvent", None, None, None)
+
+               self.mainLoop = gobject.MainLoop()
+               self.mainLoop.run()
                return False
-    
-    def smsReceived(self, a):
-               self.dbg('snsReceived started')
-               if a[0].has_key('message-type'):
-                       if self.missedLastSMS == self.getMissedCallsCount(True):
-                               if self.msgType == "Call":
-                                       self.msgType = "Both"
-                               else:
-                                       self.msgType = "SMS"
-                               self.show()
-                       self.missedLastSMS = self.getMissedCallsCount(True)
-       
-    def smsRead(self, a):
-               self.dbg('smsRead started')
+
+       def newEvent(self, a, b, c, d, e, f):
+               self.dbg('newEvent started')
+               # On NewEvent the notifications.db is not immediately filled, thus check the event after one minute waiting
+               self.tmr_main = gobject.timeout_add(60000, self.handleMissed) 
+               return True
+
+       def notificationClosed(self, a):
+               self.dbg('notificationClosed started')
                self.stop_notification(a)
        
-    def handleMissedCall(self):        
-               self.dbg('handleMissedCall started')
-               #self.dbg('self.missedLastCall: ' + self.missedLastCall)
-               #self.dbg('self.getMissedCallsCount(False): ' + self.getMissedCallsCount(False))
-               if self.missedLastCall != self.getMissedCallsCount(False):
-                       if self.msgType == "SMS":
-                               self.msgType = "Both"
-                       else:
-                               self.msgType = "Call"
+       def pendingMessagesRemoved(self, a):
+               self.dbg('pendingMessagesRemoved started')
+               self.stop_notification(self)
+
+       def handleMissed(self):
+               missedCall = self.getMissedCallsCount(False)
+               missedSMS = self.getMissedCallsCount(True)
+               self.dbg('Missed CALL: ' + str(missedCall))
+               self.dbg('Missed SMS: ' + str(missedSMS))
+
+               if missedCall and missedSMS:
+                       self.msgType = "Both"
+                       self.dbg('***********handleMissed BOTH started***********: ' + str(missedCall) + str(missedSMS))
+               elif missedCall and not missedSMS:
+                       self.msgType = "Call"
+                       self.dbg('***********handleMissed CALL started***********: ' + str(missedCall))
+               elif missedSMS and not missedCall:
+                       self.msgType = "SMS"
+                       self.dbg('***********handleMissed SMS started***********: ' + str(missedSMS))
+                       
+               if missedCall or missedSMS:
                        self.show()
-                       self.missedLastCall = self.getMissedCallsCount(False)
-               return True
-       
-    def stop_notification(self, a):
+                       
+               # Execute the function only once on NewEvent
+               return False
+               
+       def stop_notification(self, a):
                self.dbg('stop_notification started')
                try:
                        self.set_status_area_icon(None)
                        # Reset the notification (get recent missed call count)
-                       self.missed = self.getMissedCallsCount(False)
-                       self.missedSMS = self.getMissedCallsCount(True)
-                       self.missedLastCall = self.missed
-                       self.missedLastSMS = self.missedSMS
                        self.stop = False
                        self.msgType = ""
                        gobject.source_remove(self.tmr_ptr)
                        gobject.source_remove(self.tmr_ptr2)
+                       gobject.source_remove(self.tmr_main)
                except:
                        pass
 
+       def getMissedCallsCount(self, isSms):
+               conn = sqlite3.connect(self.path)
+               cur = conn.cursor()
+               if isSms:
+                       cur.execute("select count(id) from notifications where icon_name='general_sms'")
+               else:
+                       cur.execute("select count(id) from notifications where icon_name='general_missed'")
+               missed = cur.fetchone()[0]
 
-    def theLoop(self):
-               self.dbg('theLoop started')
-               missedCalls = self.getMissedCallsCount(False)
-               if self.missedLastCall != missedCalls:
-                       self.show()
-                       self.missedLastCall  = missedCalls
-               return True
-
-    def getMissedCallsCount(self, isSms):
-                self.dbg('getMissedCallsCount started. agrs: ' + str(isSms))
-                conn = sqlite3.connect(self.path)
-                cur = conn.cursor()
-                if isSms:
-                        #Nokia changed the event number from 7 to 11 and also combined the incomming and outgoing sms's
-                        cur.execute("select count(id) from Events where event_type_id = 7 and outgoing = 0")
-                else:
-                        #Nokia changed the event from 3 to 2
-                        cur.execute("select count(id) from Events where event_type_id = 3 and outgoing = 0")
-               return cur.fetchone()[0]
+               #if isSms:
+                       #self.dbg('get missed SMSs: ' + str(missed))
+               #else:
+                       #self.dbg('get missed Calls: ' + str(missed))
+               
+               return missed
 
-    def show(self):
+       def show(self):
                self.dbg('show started')
                # blink the icon every 1 second
                if not(self.stop):
@@ -359,8 +275,8 @@ class CallNotify(hildondesktop.StatusMenuItem):
                                self.tmr_ptr = gobject.timeout_add(1000, self.blinkIcon)
                        self.tmr_ptr2 = gobject.timeout_add(int(self.interval*1000*60), self.playSound)
                        
-    def blinkIcon(self):
-               self.dbg('blinkIcon started')
+       def blinkIcon(self):
+               # self.dbg('blinkIcon started')
                if self.toShow:
                        self.toShow = False
                        img = self.callPicture
@@ -371,22 +287,20 @@ class CallNotify(hildondesktop.StatusMenuItem):
                else:
                        img = self.smsPicture
                        isSMS = False
-                       counter = self.missed
                        if self.msgType == "SMS":
-                               counter = self.missedSMS
                                isSMS = True
-                       index = self.getMissedCallsCount(isSMS) - counter - 1
+                       index = self.getMissedCallsCount(isSMS) - 1
                        if index >= 5:
                                index = 5
-                               if index < 0:
-                                       index = 0
+                       if index < 0:
+                               index = 0
                        if self.msgType != "Both":
                                img = self.imgList[index]
                        self.toShow = True
                        self.set_status_area_icon(img)
                        return True
                        
-    def dbg(self, txt):
+       def dbg(self, txt):
                        if self.Debug:
                                f = open(self.configDir+'log.txt', 'a')
                                f.write(str(datetime.datetime.now()) + ': '+ txt)