3 import hildon, hildondesktop
8 import atexit, os, datetime
9 from dbus.mainloop.glib import DBusGMainLoop
15 self.idle = True # not playing at the moment
16 self.configDir = "/home/user/.config/CallNotify/"
18 # create a playbin2 pipe
19 self.player = gst.element_factory_make("playbin2", "player")
20 # connect a signal handler to it's bus
21 bus = self.player.get_bus()
22 bus.add_signal_watch()
23 bus.connect("message", self.on_message)
25 def on_message(self, bus, message):
27 if t == gst.MESSAGE_EOS:
28 self.player.set_state(gst.STATE_NULL)
30 self.dbg('Playbin2: EOS: STATE_NULL')
31 elif t == gst.MESSAGE_ERROR:
32 #err, debug = message.parse_error()
33 #print >> sys.stderr, "Error: {0} {1}".format(err, debug)
34 self.player.set_state(gst.STATE_NULL)
36 self.dbg('Playbin2: ERROR: STATE_NULL')
39 def play(self, file, volume):
40 # abort previous play if still busy
42 #print >> sys.stderr, 'audio truncated'
43 self.player.set_state(gst.STATE_NULL)
44 self.player.set_property("uri", "file://" + file)
46 self.player.set_property("volume", min(volume, 1.0))
47 self.dbg('Volume:' + str(self.player.get_property("volume")))
48 self.player.set_state(gst.STATE_PLAYING)
49 self.idle = False # now playing
53 f = open(self.configDir+'log.txt', 'a')
54 f.write(str(datetime.datetime.now()) + ': '+ txt)
59 class CallNotify(hildondesktop.StatusMenuItem):
61 hildondesktop.StatusMenuItem.__init__(self)
63 self.configDir = "/home/user/.config/CallNotify/"
64 self.configFile = "conf.txt"
65 self.configSoundFile = "sound.txt"
67 self.dbg("debugging started")
71 self.path = "/home/user/.config/hildon-desktop/notifications.db"
73 self.soundFile = "/usr/share/CallNotify/missed.wav"
74 self.soundCall = self.soundFile
75 self.soundSMS = self.soundFile
76 self.soundBoth = self.soundFile
78 self.readConfigurationFile()
80 self.dbg('constructor')
85 # Register to handle screen off/on events
86 osso_c = osso.Context("osso_test_device_on", "0.0.1", False)
87 device = osso.DeviceState(osso_c)
89 # add d-bus listener for removing notification after viewing missed call
90 # Doing timeout_add with return False instead of explicitly raising a thread
91 gobject.timeout_add(500, self.startDbusListeners)
93 self.dbg('constructor end')
95 def checkForConfigFile(self):
96 self.dbg('checkForConfigFile started')
98 if not os.path.exists(self.configDir):
99 os.mkdir(self.configDir)
100 if not os.path.exists(self.configDir+self.configFile):
101 a = open(self.configDir+self.configFile,'w+')
102 a.write('y;y;y;5.0\n')
104 if not os.path.exists(self.configDir+self.configSoundFile):
105 a = open(self.configDir+self.configSoundFile,'w+')
108 # set proper permissions
109 os.system("chmod 755 " + self.configDir)
110 os.system("chmod 644 " + self.configDir+self.configFile)
111 os.system("chmod 644 " + self.configDir+self.configSoundFile)
114 def readConfigurationFile(self):
116 self.dbg('readConfigurationFile started')
117 self.checkForConfigFile()
118 f = open(self.configDir+self.configFile, 'r')
119 raw_set = f.readline().rsplit(';')
120 self.visual = raw_set[0] in ('y')
121 self.sound = raw_set[1] in ('y')
122 self.vibration = raw_set[2] in ('y')
123 self.interval = float(raw_set[3].replace(',','.'))
126 # read sound config file
127 f = open(self.configDir+self.configSoundFile, 'r')
129 # check if specific missed call, SMS or common sound was defined in config file
131 self.soundCall = line.strip()
132 self.dbg('soundCall changed to: '+self.soundCall)
135 self.soundSMS = line.rstrip()
136 self.dbg('soundSMS changed to: '+self.soundSMS)
139 self.soundBoth = line.strip()
140 self.dbg('soundBoth changed to: '+self.soundBoth)
143 self.volume = float(line.strip())
144 self.dbg('volume changed to: '+self.volume)
147 os.remove(self.configDir+self.configFile)
148 os.remove(self.configDir+self.configSoundFile)
149 self.checkForConfigFile()
152 self.dbg('playSound started')
153 profiled = dbus.Interface(dbus.SessionBus().get_object("com.nokia.profiled", "/com/nokia/profiled"), "com.nokia.profiled")
154 mce = dbus.Interface(dbus.SystemBus().get_object("com.nokia.mce", "/com/nokia/mce/request"), "com.nokia.mce.request")
155 if self.sound and profiled.get_value("", "ringing.alert.type") != "silent":
156 # Create the player_name sink
157 if self.msgType == "Call" and profiled.get_value("", "ringing.alert.volume") != "0":
158 self.dbg('play soundCall:' + self.soundCall)
159 Playbin2().play(self.soundCall, self.volume)
160 elif self.msgType == "SMS" and profiled.get_value("", "sms.alert.volume") != "0":
161 self.dbg('play soundSMS:' + self.soundSMS)
162 Playbin2().play(self.soundSMS, self.volume)
163 elif self.msgType == "Both":
164 self.dbg('play soundBoth:' + self.soundBoth)
165 Playbin2().play(self.soundBoth, self.volume)
167 Playbin2().play(self.soundFile, self.volume)
169 if self.vibration and profiled.get_value("", "vibrating.alert.enabled") == "On":
171 mce.req_vibrator_pattern_activate("PatternIncomingCall")
173 mce.req_vibrator_pattern_deactivate("PatternIncomingCall")
176 def loadImages(self):
177 self.dbg('loadImages started')
178 icon_theme = gtk.icon_theme_get_default()
179 self.callPicture = gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/call.png",18,18)
180 self.smsPicture = gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/sms.png",18,18)
182 # Load 5 numbers and the "+5"
184 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/1.png",18,18))
185 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/2.png",18,18))
186 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/3.png",18,18))
187 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/4.png",18,18))
188 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/5.png",18,18))
189 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/more.png",18,18))
191 def startDbusListeners(self):
192 self.dbg('startDbusListeners started')
193 DBusGMainLoop(set_as_default=True)
194 bus = dbus.SessionBus()
196 bus.add_signal_receiver(self.notificationClosed, "NotificationClosed", "org.freedesktop.Notifications", None, "/org/freedesktop/Notifications")
197 bus.add_signal_receiver(self.pendingMessagesRemoved, "PendingMessagesRemoved", None, None, None)
198 bus.add_signal_receiver(self.newEvent, "NewEvent", None, None, None)
200 self.mainLoop = gobject.MainLoop()
204 def newEvent(self, a, b, c, d, e, f):
205 self.dbg('newEvent started')
206 # On NewEvent the notifications.db is not immediately filled, thus check the event after one minute waiting
207 self.tmr_main = gobject.timeout_add(20000, self.handleMissed)
210 def notificationClosed(self, a):
211 self.dbg('notificationClosed started')
212 self.stop_notification(a)
214 def pendingMessagesRemoved(self, a):
215 self.dbg('pendingMessagesRemoved started')
216 self.stop_notification(self)
218 def handleMissed(self):
219 missedCall = self.getMissedCallsCount(False)
220 missedSMS = self.getMissedCallsCount(True)
221 self.dbg('Missed CALL: ' + str(missedCall))
222 self.dbg('Missed SMS: ' + str(missedSMS))
224 if missedCall and missedSMS:
225 self.msgType = "Both"
226 self.dbg('***********handleMissed BOTH started***********: ' + str(missedCall) + str(missedSMS))
227 elif missedCall and not missedSMS:
228 self.msgType = "Call"
229 self.dbg('***********handleMissed CALL started***********: ' + str(missedCall))
230 elif missedSMS and not missedCall:
232 self.dbg('***********handleMissed SMS started***********: ' + str(missedSMS))
234 if missedCall or missedSMS:
237 # Execute the function only once on NewEvent
240 def stop_notification(self, a):
241 self.dbg('stop_notification started')
243 self.set_status_area_icon(None)
244 # Reset the notification (get recent missed call count)
247 gobject.source_remove(self.tmr_ptr)
248 gobject.source_remove(self.tmr_ptr2)
249 gobject.source_remove(self.tmr_main)
253 def getMissedCallsCount(self, isSms):
254 conn = sqlite3.connect(self.path)
257 cur.execute("select count(id) from notifications where icon_name='general_sms'")
259 cur.execute("select count(id) from notifications where icon_name='general_missed'")
260 missed = cur.fetchone()[0]
263 #self.dbg('get missed SMSs: ' + str(missed))
265 #self.dbg('get missed Calls: ' + str(missed))
270 self.dbg('show started')
271 # blink the icon every 1 second
273 self.readConfigurationFile()
276 self.tmr_ptr = gobject.timeout_add(1000, self.blinkIcon)
277 self.tmr_ptr2 = gobject.timeout_add(int(self.interval*1000*60), self.playSound)
280 # self.dbg('blinkIcon started')
283 img = self.callPicture
284 if self.msgType == "SMS":
285 img = self.smsPicture
286 self.set_status_area_icon(img)
289 img = self.smsPicture
291 if self.msgType == "SMS":
293 index = self.getMissedCallsCount(isSMS) - 1
298 if self.msgType != "Both":
299 img = self.imgList[index]
301 self.set_status_area_icon(img)
306 f = open(self.configDir+'log.txt', 'a')
307 f.write(str(datetime.datetime.now()) + ': '+ txt)
312 hd_plugin_type = CallNotify
315 # Uncomment from "if __name__..." to "gtk.main()" if running from CLI as:
316 # "run-standalone.sh python CallNotify.py"
318 #if __name__=="__main__":
319 # gobject.type_register(hd_plugin_type)
320 # obj = gobject.new(hd_plugin_type, plugin_id="plugid_id")