--- /dev/null
+#!/usr/bin/env python
+
+import os
+import time
+import datetime
+
+import dbus
+import osso.alarmd as alarmd
+
+
+class AlarmHandler(object):
+
+ _INVALID_COOKIE = -1
+ _TITLE = "Dialcentral Notifications"
+ _LAUNCHER = os.path.join(os.path.dirname(__file__), "alarm_notifier.py"),
+ _REPEAT_FOREVER = -1
+ _DEFAULT_FLAGS = (
+ alarmd.ALARM_EVENT_NO_DIALOG |
+ alarmd.ALARM_EVENT_NO_SNOOZE |
+ alarmd.ALARM_EVENT_CONNECTED
+ )
+
+ def __init__(self):
+ self._recurrence = 5
+
+ bus = dbus.SystemBus()
+ self._alarmdDBus = bus.get_object("com.nokia.alarmd", "/com/nokia/alarmd");
+ self._alarmCookie = self._INVALID_COOKIE
+
+ def load_settings(self, config, sectionName):
+ self._recurrence = config.getint(sectionName, "recurrence")
+ self._alarmCookie = config.getint(sectionName, "alarmCookie")
+
+ def save_settings(self, config, sectionName):
+ config.set(sectionName, "recurrence", str(self._recurrence))
+ config.set(sectionName, "alarmCookie", str(self._alarmCookie))
+
+ def apply_settings(self, enabled, recurrence):
+ if recurrence != self._recurrence and enabled != self.isEnabled:
+ if self.isEnabled:
+ self.delete_alarm()
+ elif enabled:
+ self._set_alarm(recurrence)
+ self._recurrence = int(recurrence)
+
+ def delete_alarm(self):
+ if self._alarmCookie == self._INVALID_COOKIE:
+ return
+ deleteResult = self._alarmdDBus.del_event(dbus.Int32(self._alarmCookie))
+ self._alarmCookie = self._INVALID_COOKIE
+ assert deleteResult != -1, "Deleting of alarm event failed"
+
+ @property
+ def recurrence(self):
+ return self._recurrence
+
+ @property
+ def isEnabled(self):
+ return self._alarmCookie != self._INVALID_COOKIE
+
+ def _get_start_time(self, recurrence):
+ now = datetime.datetime.now()
+ startTimeMinute = now.minute + recurrence
+ now.replace(minute=startTimeMinute)
+ timestamp = int(time.mktime(now.timetuple()))
+ return timestamp
+
+ def _set_alarm(self, recurrence):
+ alarmTime = self._get_start_time(recurrence)
+
+ #Setup the alarm arguments so that they can be passed to the D-Bus add_event method
+ action = []
+ action.extend(['flags', self._DEFAULT_FLAGS])
+ action.extend(['title', self._TITLE])
+ action.extend(['path', self._LAUNCHER])
+ action.extend([
+ 'arguments',
+ dbus.Array(
+ [alarmTime, int(27)],
+ signature=dbus.Signature('v')
+ )
+ ]) #int(27) used in place of alarm_index
+
+ event = []
+ event.extend([dbus.ObjectPath('/AlarmdEventRecurring'), dbus.UInt32(4)])
+ event.extend(['action', dbus.ObjectPath('/AlarmdActionExec')]) #use AlarmdActionExec instead of AlarmdActionDbus
+ event.append(dbus.UInt32(len(action) / 2))
+ event.extend(action)
+ event.extend(['time', dbus.Int64(alarmTime)])
+ event.extend(['recurr_interval', dbus.UInt32(recurrence)])
+ event.extend(['recurr_count', dbus.Int32(self._REPEAT_FOREVER)])
+
+ self._alarmCookie = self._alarmdDBus.add_event(*event);
+
+
+def main():
+ import ConfigParser
+ import constants
+ try:
+ import optparse
+ except ImportError:
+ return
+
+ parser = optparse.OptionParser()
+ parser.add_option("-x", "--display", action="store_true", dest="display", help="Display data")
+ parser.add_option("-e", "--enable", action="store_true", dest="enabled", help="Whether the alarm should be enabled or not", default=False)
+ parser.add_option("-d", "--disable", action="store_false", dest="enabled", help="Whether the alarm should be enabled or not", default=False)
+ parser.add_option("-r", "--recurrence", action="store", type="int", dest="recurrence", help="How often the alarm occurs", default=5)
+ (commandOptions, commandArgs) = parser.parse_args()
+
+ alarmHandler = AlarmHandler()
+ config = ConfigParser.SafeConfigParser()
+ config.read(constants._user_settings_)
+ alarmHandler.load_settings(config, "alarm")
+
+ if commandOptions.display:
+ print "Alarm (%s) is %s for every %d minutes" % (
+ alarmHandler._alarmCookie,
+ "enabled" if alarmHandler.isEnabled else "disabled",
+ alarmHandler.recurrence,
+ )
+ else:
+ isEnabled = commandOptions.enabled
+ recurrence = commandOptions.recurrence
+ alarmHandler.apply_settings(isEnabled, recurrence)
+
+ alarmHandler.save_settings(config, "alarm")
+ configFile = open(constants._user_settings_, "wb")
+ try:
+ config.write(configFile)
+ finally:
+ configFile.close()
+
+
+if __name__ == "__main__":
+ main()
--- /dev/null
+#!/usr/bin/env python
+
+import os
+import filecmp
+import ConfigParser
+import pprint
+
+import constants
+import gv_backend
+
+
+def get_missed(backend):
+ missedPage = backend._browser.download(backend._missedCallsURL)
+ missedJson = pprint.pformat(backend._grab_json(missedPage))
+ return missedJson
+
+
+def get_voicemail(backend):
+ voicemailPage = backend._browser.download(backend._voicemailURL)
+ voicemailJson = pprint.pformat(backend._grab_json(voicemailPage))
+ return voicemailJson
+
+
+def get_sms(backend):
+ smsPage = backend._browser.download(backend._smsURL)
+ smsJson = pprint.pformat(backend._grab_json(smsPage))
+ return smsJson
+
+
+def is_changed(backend, type, get_material):
+ currentMaterial = get_material(backend)
+ previousSnapshotPath = os.path.join(constants._data_path_, "snapshot_%s.old.json" % type)
+ currentSnapshotPath = os.path.join(constants._data_path_, "snapshot_%s.json" % type)
+
+ try:
+ os.remove(previousSnapshotPath)
+ except OSError, e:
+ # check if failed purely because the old file didn't exist, which is fine
+ if e.errno != 2:
+ raise
+ try:
+ os.rename(currentSnapshotPath, previousSnapshotPath)
+ previousExists = True
+ except OSError, e:
+ # check if failed purely because the old file didn't exist, which is fine
+ if e.errno != 2:
+ raise
+ previousExists = False
+
+ currentSnapshot = file(currentSnapshotPath, "w")
+ try:
+ currentSnapshot.write(currentMaterial)
+ finally:
+ currentSnapshot.close()
+
+ if not previousExists:
+ return True
+
+ seemEqual = filecmp.cmp(previousSnapshotPath, currentSnapshotPath)
+ return not seemEqual
+
+
+def notify():
+ gvCookiePath = os.path.join(constants._data_path_, "gv_cookies.txt")
+ backend = gv_backend.GVDialer(gvCookiePath)
+
+ loggedIn = False
+
+ if not loggedIn:
+ loggedIn = backend.is_authed()
+
+ config = ConfigParser.SafeConfigParser()
+ config.read(constants._user_settings_)
+ if not loggedIn:
+ import base64
+ try:
+ blobs = (
+ config.get(constants.__pretty_app_name__, "bin_blob_%i" % i)
+ for i in xrange(2)
+ )
+ creds = (
+ base64.b64decode(blob)
+ for blob in blobs
+ )
+ username, password = tuple(creds)
+ loggedIn = backend.login(username, password)
+ except ConfigParser.NoOptionError, e:
+ pass
+ except ConfigParser.NoSectionError, e:
+ pass
+
+ try:
+ notifyOnMissed = config.getboolean("2 - Account Info", "notifyOnMissed")
+ notifyOnVoicemail = config.getboolean("2 - Account Info", "notifyOnVoicemail")
+ notifyOnSms = config.getboolean("2 - Account Info", "notifyOnSms")
+ except ConfigParser.NoOptionError, e:
+ notifyOnMissed = False
+ notifyOnVoicemail = False
+ notifyOnSms = False
+ except ConfigParser.NoSectionError, e:
+ notifyOnMissed = False
+ notifyOnVoicemail = False
+ notifyOnSms = False
+
+ assert loggedIn
+ notifySources = []
+ if notifyOnMissed:
+ notifySources.append(("missed", get_missed))
+ if notifyOnVoicemail:
+ notifySources.append(("voicemail", get_voicemail))
+ if notifyOnSms:
+ notifySources.append(("sms", get_sms))
+
+ notifyUser = False
+ for type, get_material in (
+ ("missed", get_missed),
+ ("voicemail", get_voicemail),
+ ("sms", get_sms),
+ ):
+ if is_changed(backend, type, get_material):
+ notifyUser = True
+
+ if notifyUser:
+ import led_handler
+ led = led_handler.LedHandler()
+ led.on()
+
+
+if __name__ == "__main__":
+ notify()
+import os
+
__pretty_app_name__ = "DialCentral"
__app_name__ = "dialcentral"
__version__ = "1.0.4"
__app_magic__ = 0xdeadbeef
+_data_path_ = os.path.join(os.path.expanduser("~"), ".dialcentral")
+_user_settings_ = "%s/settings.ini" % _data_path_
import itertools
import warnings
-import gobject
import gtk
import gtk.glade
class Dialcentral(object):
_glade_files = [
- '/usr/lib/dialcentral/dialcentral.glade',
os.path.join(os.path.dirname(__file__), "dialcentral.glade"),
os.path.join(os.path.dirname(__file__), "../lib/dialcentral.glade"),
+ '/usr/lib/dialcentral/dialcentral.glade',
]
KEYPAD_TAB = 0
GV_BACKEND = 2
BACKENDS = (NULL_BACKEND, GC_BACKEND, GV_BACKEND)
- _data_path = os.path.join(os.path.expanduser("~"), ".dialcentral")
- _user_settings = "%s/settings.ini" % _data_path
-
def __init__(self):
self._initDone = False
self._connection = None
self._messagesViews = None
self._recentViews = None
self._contactsViews = None
+ self._alarmHandler = None
+ self._ledHandler = None
self._originalCurrentLabels = []
for path in self._glade_files:
else:
pass # warnings.warn("No OSSO", UserWarning, 2)
+ try:
+ import alarm_handler
+ self._alarmHandler = alarm_handler.AlarmHandler()
+ except ImportError:
+ alarm_handler = None
+ if hildon is not None:
+ import led_handler
+ self._ledHandler = led_handler.LedHandler()
+ self._ledHandler.off()
+
# Setup maemo specifics
try:
import conic
import gc_views
try:
- os.makedirs(self._data_path)
+ os.makedirs(constants._data_path_)
except OSError, e:
if e.errno != 17:
raise
- gcCookiePath = os.path.join(self._data_path, "gc_cookies.txt")
- gvCookiePath = os.path.join(self._data_path, "gv_cookies.txt")
+ gcCookiePath = os.path.join(constants._data_path_, "gc_cookies.txt")
+ gvCookiePath = os.path.join(constants._data_path_, "gv_cookies.txt")
self._defaultBackendId = self._guess_preferred_backend((
(self.GC_BACKEND, gcCookiePath),
(self.GV_BACKEND, gvCookiePath),
})
self._accountViews.update({
self.GC_BACKEND: gc_views.AccountInfo(
- self._widgetTree, self._phoneBackends[self.GC_BACKEND], self._errorDisplay
+ self._widgetTree, self._phoneBackends[self.GC_BACKEND], None, self._errorDisplay
),
self.GV_BACKEND: gc_views.AccountInfo(
- self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
+ self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._alarmHandler, self._errorDisplay
),
})
+ self._accountViews[self.GC_BACKEND].save_everything = lambda *args: None
+ self._accountViews[self.GV_BACKEND].save_everything = self._save_settings
self._recentViews.update({
self.GC_BACKEND: gc_views.RecentCallsView(
self._widgetTree, self._phoneBackends[self.GC_BACKEND], self._errorDisplay
})
evoBackend = evo_backend.EvolutionAddressBook()
- fsContactsPath = os.path.join(self._data_path, "contacts")
+ fsContactsPath = os.path.join(constants._data_path_, "contacts")
fileBackend = file_backend.FilesystemAddressBookFactory(fsContactsPath)
for backendId in (self.GV_BACKEND, self.GC_BACKEND):
self._dialpads[backendId].number_selected = self._select_action
self._initDone = True
config = ConfigParser.SafeConfigParser()
- config.read(self._user_settings)
+ config.read(constants._user_settings_)
with gtk_toolbox.gtk_lock():
self.load_settings(config)
self._spawn_attempt_login(2)
except Exception, e:
with gtk_toolbox.gtk_lock():
- self._errorDisplay.push_exception(e)
+ self._errorDisplay.push_exception()
def attempt_login(self, numOfAttempts = 10, force = False):
"""
self._change_loggedin_status(serviceId)
except StandardError, e:
with gtk_toolbox.gtk_lock():
- self._errorDisplay.push_exception(e)
+ self._errorDisplay.push_exception()
def _spawn_attempt_login(self, *args):
self._loginSink.send(args)
for blob in blobs
)
self._credentials = tuple(creds)
+
+ if self._alarmHandler is not None:
+ self._alarmHandler.load_settings(config, "alarm")
+ except ConfigParser.NoOptionError, e:
+ warnings.warn(
+ "Settings file %s is missing section %s" % (
+ constants._user_settings_,
+ e.section,
+ ),
+ stacklevel=2
+ )
except ConfigParser.NoSectionError, e:
warnings.warn(
"Settings file %s is missing section %s" % (
- self._user_settings,
+ constants._user_settings_,
e.section,
),
stacklevel=2
sectionName = "%s - %s" % (backendId, view.name())
try:
view.load_settings(config, sectionName)
+ except ConfigParser.NoOptionError, e:
+ warnings.warn(
+ "Settings file %s is missing section %s" % (
+ constants._user_settings_,
+ e.section,
+ ),
+ stacklevel=2
+ )
except ConfigParser.NoSectionError, e:
warnings.warn(
"Settings file %s is missing section %s" % (
- self._user_settings,
+ constants._user_settings_,
e.section,
),
stacklevel=2
for i, value in enumerate(self._credentials):
blob = base64.b64encode(value)
config.set(constants.__pretty_app_name__, "bin_blob_%i" % i, blob)
+ config.add_section("alarm")
+ if self._alarmHandler is not None:
+ self._alarmHandler.save_settings(config, "alarm")
+
for backendId, view in itertools.chain(
self._dialpads.iteritems(),
self._accountViews.iteritems(),
"""
config = ConfigParser.SafeConfigParser()
self.save_settings(config)
- with open(self._user_settings, "wb") as configFile:
+ with open(constants._user_settings_, "wb") as configFile:
config.write(configFile)
def _refresh_active_tab(self):
+ if self._ledHandler is not None:
+ self._ledHandler.off()
+
pageIndex = self._notebook.get_current_page()
if pageIndex == self.CONTACTS_TAB:
self._contactsViews[self._selectedBackendId].update(force=True)
loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
except StandardError, e:
loggedIn = False
- self._errorDisplay.push_exception(e)
+ self._errorDisplay.push_exception()
return
if not loggedIn:
self._phoneBackends[self._selectedBackendId].send_sms(number, message)
dialed = True
except StandardError, e:
- self._errorDisplay.push_exception(e)
+ self._errorDisplay.push_exception()
except ValueError, e:
- self._errorDisplay.push_exception(e)
+ self._errorDisplay.push_exception()
def _on_dial_clicked(self, number):
assert number, "No number to call"
loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
except StandardError, e:
loggedIn = False
- self._errorDisplay.push_exception(e)
+ self._errorDisplay.push_exception()
return
if not loggedIn:
self._phoneBackends[self._selectedBackendId].dial(number)
dialed = True
except StandardError, e:
- self._errorDisplay.push_exception(e)
+ self._errorDisplay.push_exception()
except ValueError, e:
- self._errorDisplay.push_exception(e)
+ self._errorDisplay.push_exception()
if dialed:
self._dialpads[self._selectedBackendId].clear()
def run_dialpad():
- gtk.gdk.threads_init()
- if hildon is not None:
- gtk.set_application_name(constants.__pretty_app_name__)
- handle = Dialcentral()
- gtk.main()
+ _lock_file = os.path.join(constants._data_path_, ".lock")
+ with gtk_toolbox.flock(_lock_file, 0):
+ gtk.gdk.threads_init()
+
+ if hildon is not None:
+ gtk.set_application_name(constants.__pretty_app_name__)
+ handle = Dialcentral()
+ gtk.main()
class DummyOptions(object):
<widget class="GtkTable" id="accountview">
<property name="visible">True</property>
<property name="border_width">11</property>
- <property name="n_rows">3</property>
+ <property name="n_rows">7</property>
<property name="n_columns">2</property>
<child>
<widget class="GtkLabel" id="gcnumber_display">
<property name="visible">True</property>
+ <property name="xalign">0</property>
<property name="label" translatable="yes">No Number Available</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="gcnumber_label">
<property name="visible">True</property>
- <property name="xalign">1</property>
- <property name="xpad">5</property>
- <property name="label" translatable="yes">Google Voice Number:</property>
- <property name="justify">right</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="ypad">10</property>
+ <property name="label" translatable="yes">Account Number:</property>
</widget>
+ <packing>
+ <property name="y_options"></property>
+ </packing>
</child>
<child>
<widget class="GtkLabel" id="callback_number_label">
<property name="visible">True</property>
- <property name="xalign">1</property>
- <property name="xpad">5</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="ypad">10</property>
<property name="label" translatable="yes">Callback Number:</property>
</widget>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
+ <property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkButton" id="clearcookies">
- <property name="label" translatable="yes">Clear Account Information</property>
+ <widget class="GtkComboBoxEntry" id="callbackcombo">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <signal name="clicked" handler="on_clearcookies_clicked"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="hbox4">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkSpinButton" id="minutesEntry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ </widget>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Minutes</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ </widget>
+ <packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ </widget>
+ <packing>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <widget class="GtkCheckButton" id="missedCheckbox">
+ <property name="label" translatable="yes">Missed Calls</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkCheckButton" id="voicemailCheckbox">
+ <property name="label" translatable="yes">Voicemail</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkCheckButton" id="smsCheckbox">
+ <property name="label" translatable="yes">SMS</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkCheckButton" id="notifyCheckbox">
+ <property name="label" translatable="yes">Notifications</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="yalign">0</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
- <widget class="GtkComboBoxEntry" id="callbackcombo">
+ <widget class="GtkButton" id="clearcookies">
+ <property name="label" translatable="yes">Clear Account Information</property>
<property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="focus_on_click">False</property>
+ <signal name="clicked" handler="on_clearcookies_clicked"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
+ <widget class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ </widget>
+ <packing>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
<placeholder/>
</child>
</widget>
self._prettynumber = make_pretty(self._phonenumber)
self._numberdisplay.set_label("<span size='30000' weight='bold'>%s</span>" % (self._prettynumber))
except TypeError, e:
- self._errorDisplay.push_exception(e)
+ self._errorDisplay.push_exception()
def clear(self):
self.set_number("")
class AccountInfo(object):
- def __init__(self, widgetTree, backend, errorDisplay):
+ def __init__(self, widgetTree, backend, alarmHandler, errorDisplay):
self._errorDisplay = errorDisplay
self._backend = backend
self._isPopulated = False
+ self._alarmHandler = alarmHandler
+ self._notifyOnMissed = False
+ self._notifyOnVoicemail = False
+ self._notifyOnSms = False
self._callbackList = gtk.ListStore(gobject.TYPE_STRING)
self._accountViewNumberDisplay = widgetTree.get_widget("gcnumber_display")
self._callbackCombo = widgetTree.get_widget("callbackcombo")
self._onCallbackentryChangedId = 0
+ self._notifyCheckbox = widgetTree.get_widget("notifyCheckbox")
+ self._minutesEntry = widgetTree.get_widget("minutesEntry")
+ self._missedCheckbox = widgetTree.get_widget("missedCheckbox")
+ self._voicemailCheckbox = widgetTree.get_widget("voicemailCheckbox")
+ self._smsCheckbox = widgetTree.get_widget("smsCheckbox")
+ self._onNotifyToggled = 0
+ self._onMinutesChanged = 0
+ self._onMissedToggled = 0
+ self._onVoicemailToggled = 0
+ self._onSmsToggled = 0
+
self._defaultCallback = ""
def enable(self):
assert self._backend.is_authed(), "Attempting to enable backend while not logged in"
+
self._accountViewNumberDisplay.set_use_markup(True)
self.set_account_number("")
+
self._callbackList.clear()
self._onCallbackentryChangedId = self._callbackCombo.get_child().connect("changed", self._on_callbackentry_changed)
+
+ if self._alarmHandler is not None:
+ self._notifyCheckbox.set_active(self._alarmHandler.isEnabled)
+ self._minutesEntry.set_value(self._alarmHandler.recurrence)
+ self._missedCheckbox.set_active(self._notifyOnMissed)
+ self._voicemailCheckbox.set_active(self._notifyOnVoicemail)
+ self._smsCheckbox.set_active(self._notifyOnSms)
+ self._onNotifyToggled = self._notifyCheckbox.connect("toggled", self._on_notify_toggled)
+ self._onMinutesChanged = self._minutesEntry.connect("value-changed", self._on_minutes_changed)
+ self._onMissedToggled = self._missedCheckbox.connect("toggled", self._on_missed_toggled)
+ self._onVoicemailToggled = self._voicemailCheckbox.connect("toggled", self._on_voicemail_toggled)
+ self._onSmsToggled = self._smsCheckbox.connect("toggled", self._on_sms_toggled)
+ else:
+ self._notifyCheckbox.set_sensitive(False)
+ self._minutesEntry.set_sensitive(False)
+ self._missedCheckbox.set_sensitive(False)
+ self._voicemailCheckbox.set_sensitive(False)
+ self._smsCheckbox.set_sensitive(False)
+
self.update(force=True)
def disable(self):
self._callbackCombo.get_child().disconnect(self._onCallbackentryChangedId)
+ self._onCallbackentryChangedId = 0
- self.clear()
+ if self._alarmHandler is not None:
+ self._notifyCheckbox.disconnect(self._onNotifyToggled)
+ self._minutesEntry.disconnect(self._onMinutesChanged)
+ self._missedCheckbox.disconnect(self._onNotifyToggled)
+ self._voicemailCheckbox.disconnect(self._onNotifyToggled)
+ self._smsCheckbox.disconnect(self._onNotifyToggled)
+ self._onNotifyToggled = 0
+ self._onMinutesChanged = 0
+ self._onMissedToggled = 0
+ self._onVoicemailToggled = 0
+ self._onSmsToggled = 0
+ else:
+ self._notifyCheckbox.set_sensitive(True)
+ self._minutesEntry.set_sensitive(True)
+ self._missedCheckbox.set_sensitive(True)
+ self._voicemailCheckbox.set_sensitive(True)
+ self._smsCheckbox.set_sensitive(True)
+ self.clear()
self._callbackList.clear()
def get_selected_callback_number(self):
self.set_account_number("")
self._isPopulated = False
+ def save_everything(self):
+ raise NotImplementedError
+
@staticmethod
def name():
return "Account Info"
def load_settings(self, config, section):
self._defaultCallback = config.get(section, "callback")
+ self._notifyOnMissed = config.getboolean(section, "notifyOnMissed")
+ self._notifyOnVoicemail = config.getboolean(section, "notifyOnVoicemail")
+ self._notifyOnSms = config.getboolean(section, "notifyOnSms")
def save_settings(self, config, section):
"""
"""
callback = self.get_selected_callback_number()
config.set(section, "callback", callback)
+ config.set(section, "notifyOnMissed", repr(self._notifyOnMissed))
+ config.set(section, "notifyOnVoicemail", repr(self._notifyOnVoicemail))
+ config.set(section, "notifyOnSms", repr(self._notifyOnSms))
def _populate_callback_combo(self):
self._isPopulated = True
try:
callbackNumbers = self._backend.get_callback_numbers()
except StandardError, e:
- self._errorDisplay.push_exception(e)
+ self._errorDisplay.push_exception()
self._isPopulated = False
return
UserWarning, 2
)
except StandardError, e:
- self._errorDisplay.push_exception(e)
+ self._errorDisplay.push_exception()
+
+ def _update_alarm_settings(self):
+ try:
+ isEnabled = self._notifyCheckbox.get_active()
+ recurrence = self._minutesEntry.get_value()
+ if isEnabled != self._alarmHandler.isEnabled and recurrence != self.recurrence:
+ self._alarmHandler.apply_settings(isEnabled, recurrence)
+ finally:
+ self.save_everything()
+ self._notifyCheckbox.set_active(self._alarmHandler.isEnabled)
+ self._minutesEntry.set_value(self._alarmHandler.recurrence)
def _on_callbackentry_changed(self, *args):
text = self.get_selected_callback_number()
number = make_ugly(text)
self._set_callback_number(number)
+ self.save_everything()
+
+ def _on_notify_toggled(self, *args):
+ self._update_alarm_settings()
+
+ def _on_minutes_changed(self, *args):
+ self._update_alarm_settings()
+
+ def _on_missed_toggled(self, *args):
+ self.save_everything()
+
+ def _on_voicemail_toggled(self, *args):
+ self.save_everything()
+
+ def _on_sms_toggled(self, *args):
+ self.save_everything()
+
class RecentCallsView(object):
try:
recentItems = self._backend.get_recent()
except StandardError, e:
- self._errorDisplay.push_exception_with_lock(e)
+ self._errorDisplay.push_exception_with_lock()
self._isPopulated = False
recentItems = []
try:
messageItems = self._backend.get_messages()
except StandardError, e:
- self._errorDisplay.push_exception_with_lock(e)
+ self._errorDisplay.push_exception_with_lock()
self._isPopulated = False
messageItems = []
except StandardError, e:
contacts = []
self._isPopulated = False
- self._errorDisplay.push_exception_with_lock(e)
+ self._errorDisplay.push_exception_with_lock()
for contactId, contactName in contacts:
contactType = (addressBook.contact_source_short_name(contactId), )
self._contactsmodel.append(contactType + (contactName, "", contactId) + ("", ))
contactDetails = self._addressBook.get_contact_details(contactId)
except StandardError, e:
contactDetails = []
- self._errorDisplay.push_exception(e)
+ self._errorDisplay.push_exception()
contactPhoneNumbers = [phoneNumber for phoneNumber in contactDetails]
if len(contactPhoneNumbers) == 0:
from __future__ import with_statement
+import os
+import errno
import sys
+import time
import traceback
import functools
import contextlib
@contextlib.contextmanager
+def flock(path, timeout=-1):
+ WAIT_FOREVER = -1
+ DELAY = 0.1
+ timeSpent = 0
+
+ acquired = False
+
+ while timeSpent <= timeout or timeout == WAIT_FOREVER:
+ try:
+ fd = os.open(path, os.O_CREAT | os.O_EXCL | os.O_RDWR)
+ acquired = True
+ break
+ except OSError, e:
+ if e.errno != errno.EEXIST:
+ raise
+ time.sleep(DELAY)
+ timeSpent += DELAY
+
+ assert acquired, "Failed to grab file-lock %s within timeout %d" % (path, timeout)
+
+ try:
+ yield fd
+ finally:
+ os.unlink(path)
+
+
+@contextlib.contextmanager
def gtk_lock():
gtk.gdk.threads_enter()
try:
else:
self.__show_message(message)
- def push_exception_with_lock(self, exception = None):
+ def push_exception_with_lock(self, exception = None, stacklevel=3):
with gtk_lock():
- self.push_exception(exception)
+ self.push_exception(exception, stacklevel=stacklevel)
- def push_exception(self, exception = None):
+ def push_exception(self, exception = None, stacklevel=2):
if exception is None:
userMessage = str(sys.exc_value)
warningMessage = str(traceback.format_exc())
userMessage = str(exception)
warningMessage = str(exception)
self.push_message(userMessage)
- warnings.warn(warningMessage, stacklevel=2)
+ warnings.warn(warningMessage, stacklevel=stacklevel)
def pop_message(self):
if 0 < len(self.__messages):
--- /dev/null
+#!/usr/bin/env python
+
+import dbus
+
+
+class LedHandler(object):
+
+ def __init__(self):
+ self._bus = dbus.SystemBus()
+ self._rawMceRequest = self._bus.get_object("com.nokia.mce", "/com/nokia/mce/request")
+ self._mceRequest = dbus.Interface(self._rawMceRequest, dbus_interface="com.nokia.mce.request")
+
+ self._ledPattern = "PatternCommunicationChat"
+
+ def on(self):
+ self._mceRequest.req_led_pattern_activate(self._ledPattern)
+
+ def off(self):
+ self._mceRequest.req_led_pattern_deactivate(self._ledPattern)
+
+
+if __name__ == "__main__":
+ leds = LedHandler()
+ leds.off()
self._callbackCombo = widgetTree.get_widget("callbackcombo")
self._clearCookiesButton = widgetTree.get_widget("clearcookies")
+ self._notifyCheckbox = widgetTree.get_widget("notifyCheckbox")
+ self._minutesEntry = widgetTree.get_widget("minutesEntry")
+ self._missedCheckbox = widgetTree.get_widget("missedCheckbox")
+ self._voicemailCheckbox = widgetTree.get_widget("voicemailCheckbox")
+ self._smsCheckbox = widgetTree.get_widget("smsCheckbox")
+
def enable(self):
self._callbackCombo.set_sensitive(False)
self._clearCookiesButton.set_sensitive(False)
+ self._notifyCheckbox.set_sensitive(False)
+ self._minutesEntry.set_sensitive(False)
+ self._missedCheckbox.set_sensitive(False)
+ self._voicemailCheckbox.set_sensitive(False)
+ self._smsCheckbox.set_sensitive(False)
+
self._accountViewNumberDisplay.set_text("")
def disable(self):
- self._clearCookiesButton.set_sensitive(True)
self._callbackCombo.set_sensitive(True)
+ self._clearCookiesButton.set_sensitive(True)
+
+ self._notifyCheckbox.set_sensitive(True)
+ self._minutesEntry.set_sensitive(True)
+ self._missedCheckbox.set_sensitive(True)
+ self._voicemailCheckbox.set_sensitive(True)
+ self._smsCheckbox.set_sensitive(True)
@staticmethod
def update():
* "Back" button and tabs now visually indicate when they've entered a "hold" state
* Fixed the duplicate title on Maemo
* Removing some device connection observer code due to high bug to low benefit ratio
+* Notification support
1.0.3
* Holding down a tab for a second will now force a refresh