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
81 self.interval = float(5.0)
82 self.readConfigurationFile()
84 self.dbg('constructor')
89 # Register to handle screen off/on events
90 osso_c = osso.Context("osso_test_device_on", "0.0.1", False)
91 device = osso.DeviceState(osso_c)
93 # add d-bus listener for removing notification after viewing missed call
94 # Doing timeout_add with return False instead of explicitly raising a thread
95 gobject.timeout_add(500, self.startDbusListeners)
97 self.dbg('constructor end')
99 def checkForConfigFile(self):
100 self.dbg('checkForConfigFile started')
102 if not os.path.exists(self.configDir):
103 os.mkdir(self.configDir)
104 if not os.path.exists(self.configDir+self.configFile):
105 a = open(self.configDir+self.configFile,'w+')
106 a.write('y;y;y;5.0\n')
108 if not os.path.exists(self.configDir+self.configSoundFile):
109 a = open(self.configDir+self.configSoundFile,'w+')
112 # set proper permissions
113 os.system("chmod 755 " + self.configDir)
114 os.system("chmod 644 " + self.configDir+self.configFile)
115 os.system("chmod 644 " + self.configDir+self.configSoundFile)
118 def readConfigurationFile(self):
120 self.dbg('readConfigurationFile started')
121 self.checkForConfigFile()
122 f = open(self.configDir+self.configFile, 'r')
123 raw_set = f.readline().rsplit(';')
124 self.visual = raw_set[0] in ('y')
125 self.sound = raw_set[1] in ('y')
126 self.vibration = raw_set[2] in ('y')
127 self.interval = float(raw_set[3].replace(',','.'))
128 self.dbg('visual='+str(self.visual)+' sound='+str(self.sound)+' vibration='+str(self.vibration)+' interval='+str(self.interval))
131 # read sound config file
132 f = open(self.configDir+self.configSoundFile, 'r')
134 # check if specific missed call, SMS or common sound was defined in config file
136 self.soundCall = line.strip()
137 self.dbg('soundCall changed to: '+self.soundCall)
140 self.soundSMS = line.rstrip()
141 self.dbg('soundSMS changed to: '+self.soundSMS)
144 self.soundBoth = line.strip()
145 self.dbg('soundBoth changed to: '+self.soundBoth)
148 self.volume = float(line.strip())
149 self.dbg('volume changed to: '+self.volume)
152 os.remove(self.configDir+self.configFile)
153 os.remove(self.configDir+self.configSoundFile)
154 self.checkForConfigFile()
157 self.dbg('playSound started')
158 profiled = dbus.Interface(dbus.SessionBus().get_object("com.nokia.profiled", "/com/nokia/profiled"), "com.nokia.profiled")
159 mce = dbus.Interface(dbus.SystemBus().get_object("com.nokia.mce", "/com/nokia/mce/request"), "com.nokia.mce.request")
160 if self.sound and profiled.get_value("", "ringing.alert.type") != "silent":
161 # Create the player_name sink
162 if self.msgType == "Call" and profiled.get_value("", "ringing.alert.volume") != "0":
163 self.dbg('play soundCall:' + self.soundCall)
164 Playbin2().play(self.soundCall, self.volume)
165 elif self.msgType == "SMS" and profiled.get_value("", "sms.alert.volume") != "0":
166 self.dbg('play soundSMS:' + self.soundSMS)
167 Playbin2().play(self.soundSMS, self.volume)
168 elif self.msgType == "Both":
169 self.dbg('play soundBoth:' + self.soundBoth)
170 Playbin2().play(self.soundBoth, self.volume)
172 Playbin2().play(self.soundFile, self.volume)
174 if self.vibration and profiled.get_value("", "vibrating.alert.enabled") == "On":
176 mce.req_vibrator_pattern_activate("PatternIncomingCall")
178 mce.req_vibrator_pattern_deactivate("PatternIncomingCall")
181 def loadImages(self):
182 self.dbg('loadImages started')
183 icon_theme = gtk.icon_theme_get_default()
184 self.callPicture = gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/call.png",18,18)
185 self.smsPicture = gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/sms.png",18,18)
187 # Load 5 numbers and the "+5"
189 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/1.png",18,18))
190 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/2.png",18,18))
191 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/3.png",18,18))
192 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/4.png",18,18))
193 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/5.png",18,18))
194 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/more.png",18,18))
196 def startDbusListeners(self):
197 self.dbg('startDbusListeners started')
198 DBusGMainLoop(set_as_default=True)
199 bus = dbus.SessionBus()
201 bus.add_signal_receiver(self.notificationClosed, "NotificationClosed", "org.freedesktop.Notifications", None, "/org/freedesktop/Notifications")
202 bus.add_signal_receiver(self.pendingMessagesRemoved, "PendingMessagesRemoved", None, None, None)
203 bus.add_signal_receiver(self.newEvent, "NewEvent", None, None, None)
205 self.mainLoop = gobject.MainLoop()
209 def newEvent(self, a, b, c, d, e, f):
210 self.dbg('newEvent started')
211 # On NewEvent the notifications.db is not immediately filled, thus check the event after one minute waiting
212 self.tmr_main = gobject.timeout_add(60000, self.handleMissed)
215 def notificationClosed(self, a):
216 self.dbg('notificationClosed started')
217 self.stop_notification(a)
219 def pendingMessagesRemoved(self, a):
220 self.dbg('pendingMessagesRemoved started')
221 self.stop_notification(self)
223 def handleMissed(self):
224 missedCall = self.getMissedCallsCount(False)
225 missedSMS = self.getMissedCallsCount(True)
226 self.dbg('Missed CALL: ' + str(missedCall))
227 self.dbg('Missed SMS: ' + str(missedSMS))
229 if missedCall and missedSMS:
230 self.msgType = "Both"
231 self.dbg('***********handleMissed BOTH started***********: ' + str(missedCall) + str(missedSMS))
232 elif missedCall and not missedSMS:
233 self.msgType = "Call"
234 self.dbg('***********handleMissed CALL started***********: ' + str(missedCall))
235 elif missedSMS and not missedCall:
237 self.dbg('***********handleMissed SMS started***********: ' + str(missedSMS))
239 if missedCall or missedSMS:
242 # Execute the function only once on NewEvent
245 def stop_notification(self, a):
246 self.dbg('stop_notification started')
248 self.set_status_area_icon(None)
249 # Reset the notification (get recent missed call count)
252 gobject.source_remove(self.tmr_ptr)
253 gobject.source_remove(self.tmr_ptr2)
254 gobject.source_remove(self.tmr_main)
258 def getMissedCallsCount(self, isSms):
259 conn = sqlite3.connect(self.path)
262 cur.execute("select count(id) from notifications where icon_name='general_sms'")
264 cur.execute("select count(id) from notifications where icon_name='general_missed'")
265 missed = cur.fetchone()[0]
268 #self.dbg('get missed SMSs: ' + str(missed))
270 #self.dbg('get missed Calls: ' + str(missed))
275 self.dbg('show started')
276 # blink the icon every 1 second
278 self.readConfigurationFile()
281 self.tmr_ptr = gobject.timeout_add(1000, self.blinkIcon)
282 self.tmr_ptr2 = gobject.timeout_add(int(self.interval*1000*60), self.playSound)
285 # self.dbg('blinkIcon started')
288 img = self.callPicture
289 if self.msgType == "SMS":
290 img = self.smsPicture
291 self.set_status_area_icon(img)
294 img = self.smsPicture
296 if self.msgType == "SMS":
298 index = self.getMissedCallsCount(isSMS) - 1
303 if self.msgType != "Both":
304 img = self.imgList[index]
306 self.set_status_area_icon(img)
311 f = open(self.configDir+'log.txt', 'a')
312 f.write(str(datetime.datetime.now()) + ': '+ txt)
317 hd_plugin_type = CallNotify
320 # Uncomment from "if __name__..." to "gtk.main()" if running from CLI as:
321 # "run-standalone.sh python CallNotify.py"
323 #if __name__=="__main__":
324 # gobject.type_register(hd_plugin_type)
325 # obj = gobject.new(hd_plugin_type, plugin_id="plugid_id")