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)
94 hildon.hildon_play_system_sound(self.soundFile)
95 self.dbg('constructor end')
97 def checkForConfigFile(self):
98 self.dbg('checkForConfigFile started')
100 if not os.path.exists(self.configDir):
101 os.mkdir(self.configDir)
102 if not os.path.exists(self.configDir+self.configFile):
103 a = open(self.configDir+self.configFile,'w+')
104 a.write('y;y;y;5.0\n')
106 if not os.path.exists(self.configDir+self.configSoundFile):
107 a = open(self.configDir+self.configSoundFile,'w+')
110 # set proper permissions
111 os.system("chmod 766 " + self.configDir)
112 os.system("chmod 766" + self.configDir+self.configFile)
113 os.system("chmod 766" + self.configDir+self.configSoundFile)
116 def readConfigurationFile(self):
118 self.dbg('readConfigurationFile started')
119 self.checkForConfigFile()
120 f = open(self.configDir+self.configFile, 'r')
121 raw_set = f.readline().rsplit(';')
122 self.visual = raw_set[0] in ('y')
123 self.sound = raw_set[1] in ('y')
124 self.vibration = raw_set[2] in ('y')
125 self.interval = float(raw_set[3].replace(',','.'))
128 # read sound config file
129 f = open(self.configDir+self.configSoundFile, 'r')
131 # check if specific missed call, SMS or common sound was defined in config file
133 self.soundCall = line.rstrip()
136 self.soundSMS = line.rstrip()
139 self.soundBoth = line.rstrip()
142 self.volume = float(line.rstrip())
145 os.remove(self.configDir+self.configFile)
146 os.remove(self.configDir+self.configSoundFile)
147 self.checkForConfigFile()
150 self.dbg('playSound started')
151 profiled = dbus.Interface(dbus.SessionBus().get_object("com.nokia.profiled", "/com/nokia/profiled"), "com.nokia.profiled")
152 mce = dbus.Interface(dbus.SystemBus().get_object("com.nokia.mce", "/com/nokia/mce/request"), "com.nokia.mce.request")
153 if self.sound and profiled.get_value("", "ringing.alert.type") != "silent":
154 # Create the player_name sink
155 if self.msgType == "Call" and profiled.get_value("", "ringing.alert.volume") != "0":
156 self.dbg('play soundCall:' + self.soundCall)
157 Playbin2().play(self.soundCall, self.volume)
158 elif self.msgType == "SMS" and profiled.get_value("", "sms.alert.volume") != "0":
159 self.dbg('play soundSMS:' + self.soundSMS)
160 Playbin2().play(self.soundSMS, self.volume)
161 elif self.msgType == "Both":
162 self.dbg('play soundBoth:' + self.soundBoth)
163 Playbin2().play(self.soundBoth, self.volume)
165 Playbin2().play(self.soundFile, self.volume)
167 if self.vibration and profiled.get_value("", "vibrating.alert.enabled") == "On":
168 mce.req_vibrator_pattern_activate("PatternIncomingCall")
169 mce.req_vibrator_pattern_deactivate("PatternIncomingCall")
172 def loadImages(self):
173 self.dbg('loadImages started')
174 icon_theme = gtk.icon_theme_get_default()
175 self.callPicture = gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/call.png",18,18)
176 self.smsPicture = gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/sms.png",18,18)
178 # Load 5 numbers and the "+5"
180 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/1.png",18,18))
181 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/2.png",18,18))
182 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/3.png",18,18))
183 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/4.png",18,18))
184 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/5.png",18,18))
185 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/more.png",18,18))
187 def startDbusListeners(self):
188 self.dbg('startDbusListeners started')
189 DBusGMainLoop(set_as_default=True)
190 bus = dbus.SessionBus()
192 bus.add_signal_receiver(self.notificationClosed, "NotificationClosed", "org.freedesktop.Notifications", None, "/org/freedesktop/Notifications")
193 bus.add_signal_receiver(self.pendingMessagesRemoved, "PendingMessagesRemoved", None, None, None)
194 bus.add_signal_receiver(self.newEvent, "NewEvent", None, None, None)
196 self.mainLoop = gobject.MainLoop()
200 def newEvent(self, a, b, c, d, e, f):
201 self.dbg('newEvent started')
202 # On NewEvent the notifications.db is not immediately filled, thus check the event after one minute waiting
203 self.tmr_main = gobject.timeout_add(60000, self.handleMissed)
206 def notificationClosed(self, a):
207 self.dbg('notificationClosed started')
208 self.stop_notification(a)
210 def pendingMessagesRemoved(self, a):
211 self.dbg('pendingMessagesRemoved started')
212 self.stop_notification(self)
214 def handleMissed(self):
215 missedCall = self.getMissedCallsCount(False)
216 missedSMS = self.getMissedCallsCount(True)
217 self.dbg('Missed CALL: ' + str(missedCall))
218 self.dbg('Missed SMS: ' + str(missedSMS))
220 if missedCall and missedSMS:
221 self.msgType = "Both"
222 self.dbg('***********handleMissed BOTH started***********: ' + str(missedCall) + str(missedSMS))
223 elif missedCall and not missedSMS:
224 self.msgType = "Call"
225 self.dbg('***********handleMissed CALL started***********: ' + str(missedCall))
226 elif missedSMS and not missedCall:
228 self.dbg('***********handleMissed SMS started***********: ' + str(missedSMS))
230 if missedCall or missedSMS:
233 # Execute the function only once on NewEvent
236 def stop_notification(self, a):
237 self.dbg('stop_notification started')
239 self.set_status_area_icon(None)
240 # Reset the notification (get recent missed call count)
243 gobject.source_remove(self.tmr_ptr)
244 gobject.source_remove(self.tmr_ptr2)
245 gobject.source_remove(self.tmr_main)
249 def getMissedCallsCount(self, isSms):
250 conn = sqlite3.connect(self.path)
253 cur.execute("select count(id) from notifications where icon_name='general_sms'")
255 cur.execute("select count(id) from notifications where icon_name='general_missed'")
256 missed = cur.fetchone()[0]
259 #self.dbg('get missed SMSs: ' + str(missed))
261 #self.dbg('get missed Calls: ' + str(missed))
266 self.dbg('show started')
267 # blink the icon every 1 second
269 self.readConfigurationFile()
272 self.tmr_ptr = gobject.timeout_add(1000, self.blinkIcon)
273 self.tmr_ptr2 = gobject.timeout_add(int(self.interval*1000*60), self.playSound)
276 # self.dbg('blinkIcon started')
279 img = self.callPicture
280 if self.msgType == "SMS":
281 img = self.smsPicture
282 self.set_status_area_icon(img)
285 img = self.smsPicture
287 if self.msgType == "SMS":
289 index = self.getMissedCallsCount(isSMS) - 1
294 if self.msgType != "Both":
295 img = self.imgList[index]
297 self.set_status_area_icon(img)
302 f = open(self.configDir+'log.txt', 'a')
303 f.write(str(datetime.datetime.now()) + ': '+ txt)
308 hd_plugin_type = CallNotify
311 # Uncomment from "if __name__..." to "gtk.main()" if running from CLI as:
312 # "run-standalone.sh python CallNotify.py"
314 #if __name__=="__main__":
315 # gobject.type_register(hd_plugin_type)
316 # obj = gobject.new(hd_plugin_type, plugin_id="plugid_id")