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')
152 # Create the player_name sink
153 if self.msgType == "Call":
154 self.dbg('play soundCall:' + self.soundCall)
155 Playbin2().play(self.soundCall, self.volume)
156 elif self.msgType == "SMS":
157 self.dbg('play soundSMS:' + self.soundSMS)
158 Playbin2().play(self.soundSMS, self.volume)
159 elif self.msgType == "Both":
160 self.dbg('play soundBoth:' + self.soundBoth)
161 Playbin2().play(self.soundBoth, self.volume)
163 Playbin2().play(self.soundFile, self.volume)
166 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\'"
170 bb = 'run-standalone.sh dbus-send --print-reply --system --dest=com.nokia.mce /com/nokia/mce/request com.nokia.mce.request.req_vibrator_pattern_deactivate string:' + "\'PatternIncomingCall\'"
175 def loadImages(self):
176 self.dbg('loadImages started')
177 icon_theme = gtk.icon_theme_get_default()
178 self.callPicture = gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/call.png",18,18)
179 self.smsPicture = gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/sms.png",18,18)
181 # Load 5 numbers and the "+5"
183 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/1.png",18,18))
184 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/2.png",18,18))
185 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/3.png",18,18))
186 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/4.png",18,18))
187 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/5.png",18,18))
188 self.imgList.append(gtk.gdk.pixbuf_new_from_file_at_size("/usr/share/CallNotify/more.png",18,18))
190 def startDbusListeners(self):
191 self.dbg('startDbusListeners started')
192 DBusGMainLoop(set_as_default=True)
193 bus = dbus.SessionBus()
195 bus.add_signal_receiver(self.notificationClosed, "NotificationClosed", "org.freedesktop.Notifications", None, "/org/freedesktop/Notifications")
196 bus.add_signal_receiver(self.pendingMessagesRemoved, "PendingMessagesRemoved", None, None, None)
197 bus.add_signal_receiver(self.newEvent, "NewEvent", None, None, None)
199 self.mainLoop = gobject.MainLoop()
203 def newEvent(self, a, b, c, d, e, f):
204 self.dbg('newEvent started')
205 # On NewEvent the notifications.db is not immediately filled, thus check the event after one minute waiting
206 self.tmr_main = gobject.timeout_add(60000, self.handleMissed)
209 def notificationClosed(self, a):
210 self.dbg('notificationClosed started')
211 self.stop_notification(a)
213 def pendingMessagesRemoved(self, a):
214 self.dbg('pendingMessagesRemoved started')
215 self.stop_notification(self)
217 def handleMissed(self):
218 missedCall = self.getMissedCallsCount(False)
219 missedSMS = self.getMissedCallsCount(True)
220 self.dbg('Missed CALL: ' + str(missedCall))
221 self.dbg('Missed SMS: ' + str(missedSMS))
223 if missedCall and missedSMS:
224 self.msgType = "Both"
225 self.dbg('***********handleMissed BOTH started***********: ' + str(missedCall) + str(missedSMS))
226 elif missedCall and not missedSMS:
227 self.msgType = "Call"
228 self.dbg('***********handleMissed CALL started***********: ' + str(missedCall))
229 elif missedSMS and not missedCall:
231 self.dbg('***********handleMissed SMS started***********: ' + str(missedSMS))
233 if missedCall or missedSMS:
236 # Execute the function only once on NewEvent
239 def stop_notification(self, a):
240 self.dbg('stop_notification started')
242 self.set_status_area_icon(None)
243 # Reset the notification (get recent missed call count)
246 gobject.source_remove(self.tmr_ptr)
247 gobject.source_remove(self.tmr_ptr2)
248 gobject.source_remove(self.tmr_main)
252 def getMissedCallsCount(self, isSms):
253 conn = sqlite3.connect(self.path)
256 cur.execute("select count(id) from notifications where icon_name='general_sms'")
258 cur.execute("select count(id) from notifications where icon_name='general_missed'")
259 missed = cur.fetchone()[0]
262 #self.dbg('get missed SMSs: ' + str(missed))
264 #self.dbg('get missed Calls: ' + str(missed))
269 self.dbg('show started')
270 # blink the icon every 1 second
272 self.readConfigurationFile()
275 self.tmr_ptr = gobject.timeout_add(1000, self.blinkIcon)
276 self.tmr_ptr2 = gobject.timeout_add(int(self.interval*1000*60), self.playSound)
279 # self.dbg('blinkIcon started')
282 img = self.callPicture
283 if self.msgType == "SMS":
284 img = self.smsPicture
285 self.set_status_area_icon(img)
288 img = self.smsPicture
290 if self.msgType == "SMS":
292 index = self.getMissedCallsCount(isSMS) - 1
297 if self.msgType != "Both":
298 img = self.imgList[index]
300 self.set_status_area_icon(img)
305 f = open(self.configDir+'log.txt', 'a')
306 f.write(str(datetime.datetime.now()) + ': '+ txt)
311 hd_plugin_type = CallNotify
314 # Uncomment from "if __name__..." to "gtk.main()" if running from CLI as:
315 # "run-standalone.sh python CallNotify.py"
317 #if __name__=="__main__":
318 # gobject.type_register(hd_plugin_type)
319 # obj = gobject.new(hd_plugin_type, plugin_id="plugid_id")