Import patched yrannadx version from http://talk.maemo.org/showpost.php?p=1164866...
authorPali Rohár <pali.rohar@gmail.com>
Fri, 22 Jun 2012 13:10:58 +0000 (15:10 +0200)
committerPali Rohár <pali.rohar@gmail.com>
Fri, 22 Jun 2012 13:10:58 +0000 (15:10 +0200)
Changes:
- It uses playbin2 for playing sounds for missed calls and sms, and this means any sound file can be used instead of default one /usr/share/CallNotify/missed.wav
- If you want to use separate sound files for missed call, sms and both, you need to copy the attached sound.txt file to /home/user/.config/CallNotify/ directory. The file contain in order: first line is for missed call sound, second line is for missed sms sound and third line is for missed both call and sms sound. Of course you need to change the lines with your own sound files. Last line is volume in the range 0.1-1.0.

src/usr/lib/hildon-desktop/CallNotify.py [changed mode: 0755->0644]

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)