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)
96 gobject.timeout_add(100, self.handleMissed)
98 self.dbg('constructor end')
100 def checkForConfigFile(self):
101 self.dbg('checkForConfigFile started')
103 if not os.path.exists(self.configDir):
104 os.mkdir(self.configDir)
105 if not os.path.exists(self.configDir+self.configFile):
106 a = open(self.configDir+self.configFile,'w+')
107 a.write('y;y;y;5.0\n')
109 if not os.path.exists(self.configDir+self.configSoundFile):
110 a = open(self.configDir+self.configSoundFile,'w+')
113 # set proper permissions
114 os.system("chmod 755 " + self.configDir)
115 os.system("chmod 644 " + self.configDir+self.configFile)
116 os.system("chmod 644 " + self.configDir+self.configSoundFile)
119 def readConfigurationFile(self):
121 self.dbg('readConfigurationFile started')
122 self.checkForConfigFile()
123 f = open(self.configDir+self.configFile, 'r')
124 raw_set = f.readline().rsplit(';')
125 self.visual = raw_set[0] in ('y')
126 self.sound = raw_set[1] in ('y')
127 self.vibration = raw_set[2] in ('y')
128 self.interval = float(raw_set[3].replace(',','.'))
129 self.dbg('visual='+str(self.visual)+' sound='+str(self.sound)+' vibration='+str(self.vibration)+' interval='+str(self.interval))
132 # read sound config file
133 f = open(self.configDir+self.configSoundFile, 'r')
135 # check if specific missed call, SMS or common sound was defined in config file
137 self.soundCall = line.strip()
138 self.dbg('soundCall changed to: '+self.soundCall)
141 self.soundSMS = line.rstrip()
142 self.dbg('soundSMS changed to: '+self.soundSMS)
145 self.soundBoth = line.strip()
146 self.dbg('soundBoth changed to: '+self.soundBoth)
149 self.volume = float(line.strip())
150 self.dbg('volume changed to: '+self.volume)
153 os.remove(self.configDir+self.configFile)
154 os.remove(self.configDir+self.configSoundFile)
155 self.checkForConfigFile()
158 self.dbg('playSound started')
159 profiled = dbus.Interface(dbus.SessionBus().get_object("com.nokia.profiled", "/com/nokia/profiled"), "com.nokia.profiled")
160 mce = dbus.Interface(dbus.SystemBus().get_object("com.nokia.mce", "/com/nokia/mce/request"), "com.nokia.mce.request")
161 if self.sound and profiled.get_value("", "ringing.alert.type") != "silent":
162 # Create the player_name sink
163 if self.msgType == "Call" and profiled.get_value("", "ringing.alert.volume") != "0":
164 self.dbg('play soundCall:' + self.soundCall)
165 Playbin2().play(self.soundCall, self.volume)
166 elif self.msgType == "SMS" and profiled.get_value("", "sms.alert.volume") != "0":
167 self.dbg('play soundSMS:' + self.soundSMS)
168 Playbin2().play(self.soundSMS, self.volume)
169 elif self.msgType == "Both":
170 self.dbg('play soundBoth:' + self.soundBoth)
171 Playbin2().play(self.soundBoth, self.volume)
173 Playbin2().play(self.soundFile, self.volume)
175 if self.vibration and profiled.get_value("", "vibrating.alert.enabled") == "On":
177 mce.req_vibrator_pattern_activate("PatternIncomingCall")
179 mce.req_vibrator_pattern_deactivate("PatternIncomingCall")
182 def loadImages(self):
183 self.dbg('loadImages started')
184 icon_theme = gtk.icon_theme_get_default()
185 self.callPicture = gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/call.png",18,18)
186 self.smsPicture = gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/sms.png",18,18)
188 # Load 5 numbers and the "+5"
190 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/1.png",18,18))
191 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/2.png",18,18))
192 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/3.png",18,18))
193 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/4.png",18,18))
194 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/5.png",18,18))
195 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/more.png",18,18))
197 def startDbusListeners(self):
198 self.dbg('startDbusListeners started')
199 DBusGMainLoop(set_as_default=True)
200 bus = dbus.SessionBus()
202 bus.add_signal_receiver(self.notificationClosed, "NotificationClosed", "org.freedesktop.Notifications", None, "/org/freedesktop/Notifications")
203 bus.add_signal_receiver(self.pendingMessagesRemoved, "PendingMessagesRemoved", None, None, None)
204 bus.add_signal_receiver(self.newEvent, "NewEvent", None, None, None)
206 self.mainLoop = gobject.MainLoop()
210 def newEvent(self, a, b, c, d, e, f):
211 self.dbg('newEvent started')
212 # On NewEvent the notifications.db is not immediately filled, thus check the event after one minute waiting
213 self.tmr_main = gobject.timeout_add(20000, self.handleMissed)
216 def notificationClosed(self, a):
217 self.dbg('notificationClosed started')
218 self.stop_notification(a)
220 def pendingMessagesRemoved(self, a):
221 self.dbg('pendingMessagesRemoved started')
222 self.stop_notification(self)
224 def handleMissed(self):
225 missedCall = self.getMissedCallsCount(False)
226 missedSMS = self.getMissedCallsCount(True)
227 self.dbg('Missed CALL: ' + str(missedCall))
228 self.dbg('Missed SMS: ' + str(missedSMS))
230 if missedCall and missedSMS:
231 self.msgType = "Both"
232 self.dbg('***********handleMissed BOTH started***********: ' + str(missedCall) + str(missedSMS))
233 elif missedCall and not missedSMS:
234 self.msgType = "Call"
235 self.dbg('***********handleMissed CALL started***********: ' + str(missedCall))
236 elif missedSMS and not missedCall:
238 self.dbg('***********handleMissed SMS started***********: ' + str(missedSMS))
240 if missedCall or missedSMS:
243 # Execute the function only once on NewEvent
246 def stop_notification(self, a):
247 self.dbg('stop_notification started')
249 self.set_status_area_icon(None)
250 # Reset the notification (get recent missed call count)
253 gobject.source_remove(self.tmr_ptr)
254 gobject.source_remove(self.tmr_ptr2)
255 gobject.source_remove(self.tmr_main)
259 def getMissedCallsCount(self, isSms):
260 conn = sqlite3.connect(self.path)
263 cur.execute("select count(id) from notifications where icon_name='general_sms'")
265 cur.execute("select count(id) from notifications where icon_name='general_missed'")
266 missed = cur.fetchone()[0]
269 #self.dbg('get missed SMSs: ' + str(missed))
271 #self.dbg('get missed Calls: ' + str(missed))
276 self.dbg('show started')
277 # blink the icon every 1 second
279 self.readConfigurationFile()
282 self.tmr_ptr = gobject.timeout_add(1000, self.blinkIcon)
283 self.tmr_ptr2 = gobject.timeout_add(int(self.interval*1000*60), self.playSound)
286 # self.dbg('blinkIcon started')
289 img = self.callPicture
290 if self.msgType == "SMS":
291 img = self.smsPicture
292 self.set_status_area_icon(img)
295 img = self.smsPicture
297 if self.msgType == "SMS":
299 index = self.getMissedCallsCount(isSMS) - 1
304 if self.msgType != "Both":
305 img = self.imgList[index]
307 self.set_status_area_icon(img)
312 f = open(self.configDir+'log.txt', 'a')
313 f.write(str(datetime.datetime.now()) + ': '+ txt)
318 hd_plugin_type = CallNotify
321 # Uncomment from "if __name__..." to "gtk.main()" if running from CLI as:
322 # "run-standalone.sh python CallNotify.py"
324 #if __name__=="__main__":
325 # gobject.type_register(hd_plugin_type)
326 # obj = gobject.new(hd_plugin_type, plugin_id="plugid_id")