'+13456789000'
"""
uglynumber = re.sub('[^0-9+]', '', prettynumber)
+
if uglynumber.startswith("+"):
pass
- elif uglynumber.startswith("1") and len(uglynumber) == 11:
+ elif uglynumber.startswith("1"):
uglynumber = "+"+uglynumber
- elif len(uglynumber) == 10:
+ elif 10 <= len(uglynumber):
+ assert uglynumber[0] not in ("+", "1")
uglynumber = "+1"+uglynumber
else:
pass
- #validateRe = re.compile("^\+?[0-9]{10,}$")
- #assert validateRe.match(uglynumber) is not None
-
return uglynumber
-def _make_pretty_with_areacodde(phonenumber):
+def _make_pretty_with_areacode(phonenumber):
prettynumber = "(%s)" % (phonenumber[0:3], )
if 3 < len(phonenumber):
prettynumber += " %s" % (phonenumber[3:6], )
def _make_pretty_international(phonenumber):
prettynumber = phonenumber
- if phonenumber.startswith("0"):
- prettynumber = "+%s " % (phonenumber[0:3], )
- if 3 < len(phonenumber):
- prettynumber += _make_pretty_with_areacodde(phonenumber[3:])
if phonenumber.startswith("1"):
prettynumber = "1 "
- prettynumber += _make_pretty_with_areacodde(phonenumber[1:])
+ prettynumber += _make_pretty_with_areacode(phonenumber[1:])
return prettynumber
prettynumber = _make_pretty_international(phonenumber[1:])
if not prettynumber.startswith("+"):
prettynumber = "+"+prettynumber
- elif 8 < len(phonenumber) and phonenumber[0] in ("0", "1"):
+ elif 8 < len(phonenumber) and phonenumber[0] in ("1", ):
prettynumber = _make_pretty_international(phonenumber)
elif 7 < len(phonenumber):
- prettynumber = _make_pretty_with_areacodde(phonenumber)
+ prettynumber = _make_pretty_with_areacode(phonenumber)
elif 3 < len(phonenumber):
prettynumber = _make_pretty_local(phonenumber)
else:
return "\n".join(_collapse_message(messageLines, maxCharsPerLine, maxLines))
+def _get_contact_numbers(backend, contactId, number):
+ if contactId:
+ contactPhoneNumbers = list(backend.get_contact_details(contactId))
+ uglyContactNumbers = (
+ make_ugly(contactNumber)
+ for (numberDescription, contactNumber) in contactPhoneNumbers
+ )
+ defaultMatches = [
+ (
+ number == contactNumber or
+ number[1:] == contactNumber and number.startswith("1") or
+ number[2:] == contactNumber and number.startswith("+1") or
+ number == contactNumber[1:] and contactNumber.startswith("1") or
+ number == contactNumber[2:] and contactNumber.startswith("+1")
+ )
+ for contactNumber in uglyContactNumbers
+ ]
+ try:
+ defaultIndex = defaultMatches.index(True)
+ except ValueError:
+ contactPhoneNumbers.append(("Other", number))
+ defaultIndex = len(contactPhoneNumbers)-1
+ _moduleLogger.warn(
+ "Could not find contact %r's number %s among %r" % (
+ contactId, number, contactPhoneNumbers
+ )
+ )
+ else:
+ contactPhoneNumbers = [("Phone", number)]
+ defaultIndex = -1
+
+ return contactPhoneNumbers, defaultIndex
+
+
class SmsEntryWindow(object):
MAX_CHAR = 160
- def __init__(self, widgetTree):
+ def __init__(self, widgetTree, parent, app):
self._clipboard = gtk.clipboard_get()
self._widgetTree = widgetTree
+ self._parent = parent
+ self._app = app
+ self._isFullScreen = False
+
self._window = self._widgetTree.get_widget("smsWindow")
+ self._window = hildonize.hildonize_window(self._app, self._window)
+ self._window.set_title("SMS")
self._window.connect("delete-event", self._on_delete)
self._window.connect("key-press-event", self._on_key_press)
+ self._window.connect("window-state-event", self._on_window_state_change)
+ self._widgetTree.get_widget("smsMessagesViewPort").get_parent().show()
+
+ errorBox = self._widgetTree.get_widget("smsErrorEventBox")
+ errorDescription = self._widgetTree.get_widget("smsErrorDescription")
+ errorClose = self._widgetTree.get_widget("smsErrorClose")
+ self._errorDisplay = gtk_toolbox.ErrorDisplay(errorBox, errorDescription, errorClose)
self._smsButton = self._widgetTree.get_widget("sendSmsButton")
self._smsButton.connect("clicked", self._on_send)
self._contacts = []
- def add_contact(self, contactDetails, messages = (), parent = None, defaultIndex = -1):
+ def add_contact(self, name, contactDetails, messages = (), defaultIndex = -1):
contactNumbers = list(self._to_contact_numbers(contactDetails))
- assert contactNumbers
+ assert contactNumbers, "Contact must have at least one number"
contactIndex = defaultIndex if defaultIndex != -1 else 0
contact = contactNumbers, contactIndex, messages
self._contacts.append(contact)
+ nameLabel = gtk.Label(name)
selector = gtk.Button(contactNumbers[0][1])
+ if len(contactNumbers) == 1:
+ selector.set_sensitive(False)
removeContact = gtk.Button(stock="gtk-delete")
row = gtk.HBox()
+ row.pack_start(nameLabel, True, True)
row.pack_start(selector, True, True)
row.pack_start(removeContact, False, False)
row.show_all()
self._update_button_state()
self._update_context()
- if parent is not None:
- parentSize = parent.get_size()
- self._window.resize(parentSize[0], max(parentSize[1]-10, 100))
+ parentSize = self._parent.get_size()
+ self._window.resize(parentSize[0], max(parentSize[1]-10, 100))
self._window.show()
self._window.present()
self._smsEntry.grab_focus()
- dx = self._conversationView.get_allocation().height - self._conversationViewPort.get_allocation().height
- dx = max(dx, 0)
- adjustment = self._scrollWindow.get_vadjustment()
- adjustment.value = dx
+ self._scroll_to_bottom()
def clear(self):
del self._contacts[:]
- for contactNumberSelector in list(self._targetList.get_children()):
- self._targetList.remove(contactNumberSelector)
+ for row in list(self._targetList.get_children()):
+ self._targetList.remove(row)
self._smsEntry.get_buffer().set_text("")
self._update_letter_count()
self._update_context()
+ def fullscreen(self):
+ self._window.fullscreen()
+
+ def unfullscreen(self):
+ self._window.unfullscreen()
+
def _remove_contact(self, contactIndex):
del self._contacts[contactIndex]
- contactNumberSelector = list(self._targetList.get_children())[contactIndex]
- self._targetList.remove(contactNumberSelector)
+ row = list(self._targetList.get_children())[contactIndex]
+ self._targetList.remove(row)
self._update_button_state()
self._update_context()
+ self._scroll_to_bottom()
+
+ def _scroll_to_bottom(self):
+ dx = self._conversationView.get_allocation().height - self._conversationViewPort.get_allocation().height
+ dx = max(dx, 0)
+ adjustment = self._scrollWindow.get_vadjustment()
+ adjustment.value = dx
def _update_letter_count(self):
if self._smsEntrySize is None:
display = " - ".join((make_pretty(phoneNumber), phoneType))
yield (phoneNumber, display)
- def _hide(self):
+ def _pseudo_destroy(self):
self.clear()
self._window.hide()
)
self._contacts[contactIndex] = contactNumbers, index, messages
+ def send_sms(self, numbers, message):
+ raise NotImplementedError()
+
+ def dial(self, number):
+ raise NotImplementedError()
+
def _on_phone(self, *args):
try:
- assert len(self._contacts) == 1
+ assert len(self._contacts) == 1, "One and only one contact is required"
self._request_number(0)
contactNumbers, numberIndex, messages = self._contacts[0]
self._phoneButton.set_label(contactNumbers[numberIndex][1])
row = list(self._targetList.get_children())[0]
- phoneButton = list(row.get_children())[0]
+ phoneButton = list(row.get_children())[1]
phoneButton.set_label(contactNumbers[numberIndex][1])
except Exception, e:
- _moduleLogger.exception("%s" % str(e))
+ self._errorDisplay.push_exception()
def _on_choose_phone_n(self, button, row):
try:
- assert 1 < len(self._contacts)
+ assert 1 < len(self._contacts), "More than one contact required"
targetList = list(self._targetList.get_children())
index = targetList.index(row)
self._request_number(index)
contactNumbers, numberIndex, messages = self._contacts[0]
- phoneButton = list(row.get_children())[0]
+ phoneButton = list(row.get_children())[1]
phoneButton.set_label(contactNumbers[numberIndex][1])
except Exception, e:
- _moduleLogger.exception("%s" % str(e))
+ self._errorDisplay.push_exception()
def _on_remove_phone_n(self, button, row):
try:
- assert 1 < len(self._contacts)
+ assert 1 < len(self._contacts), "More than one contact required"
targetList = list(self._targetList.get_children())
index = targetList.index(row)
self._update_context()
self._update_button_state()
except Exception, e:
- _moduleLogger.exception("%s" % str(e))
+ self._errorDisplay.push_exception()
def _on_entry_changed(self, *args):
- self._update_letter_count()
+ try:
+ self._update_letter_count()
+ except Exception, e:
+ self._errorDisplay.push_exception()
def _on_send(self, *args):
- assert 0 < len(self._contacts), "%r" % self._contacts
- phoneNumbers = [
- make_ugly(contact[0][contact[1]][0])
- for contact in self._contacts
- ]
-
- entryBuffer = self._smsEntry.get_buffer()
- enteredMessage = entryBuffer.get_text(entryBuffer.get_start_iter(), entryBuffer.get_end_iter())
- enteredMessage = enteredMessage.strip()
- assert enteredMessage
- # @todo
- self._hide()
+ try:
+ assert 0 < len(self._contacts), "At least one contact required (%r)" % self._contacts
+ phoneNumbers = [
+ make_ugly(contact[0][contact[1]][0])
+ for contact in self._contacts
+ ]
+
+ entryBuffer = self._smsEntry.get_buffer()
+ enteredMessage = entryBuffer.get_text(entryBuffer.get_start_iter(), entryBuffer.get_end_iter())
+ enteredMessage = enteredMessage.strip()
+ assert enteredMessage, "No message provided"
+ self.send_sms(phoneNumbers, enteredMessage)
+ self._pseudo_destroy()
+ except Exception, e:
+ self._errorDisplay.push_exception()
def _on_dial(self, *args):
- assert len(self._contacts) == 1, "%r" % self._contacts
- contact = self._contacts[0]
- contactNumber = contact[0][contact[1]][0]
- phoneNumber = make_ugly(contactNumber)
- # @todo
- self._hide()
+ try:
+ assert len(self._contacts) == 1, "One and only one contact allowed (%r)" % self._contacts
+ contact = self._contacts[0]
+ contactNumber = contact[0][contact[1]][0]
+ phoneNumber = make_ugly(contactNumber)
+ self.dial(phoneNumber)
+ self._pseudo_destroy()
+ except Exception, e:
+ self._errorDisplay.push_exception()
def _on_delete(self, *args):
- self._window.emit_stop_by_name("delete-event")
- self._hide()
+ try:
+ self._window.emit_stop_by_name("delete-event")
+ if hildonize.IS_FREMANTLE_SUPPORTED:
+ self._window.hide()
+ else:
+ self._pseudo_destroy()
+ except Exception, e:
+ self._errorDisplay.push_exception()
return True
+ def _on_window_state_change(self, widget, event, *args):
+ try:
+ if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
+ self._isFullScreen = True
+ else:
+ self._isFullScreen = False
+ except Exception, e:
+ self._errorDisplay.push_exception()
+
def _on_key_press(self, widget, event):
+ RETURN_TYPES = (gtk.keysyms.Return, gtk.keysyms.ISO_Enter, gtk.keysyms.KP_Enter)
try:
- if event.keyval == ord("c") and event.get_state() & gtk.gdk.CONTROL_MASK:
+ if (
+ event.keyval == gtk.keysyms.F6 or
+ event.keyval in RETURN_TYPES and event.get_state() & gtk.gdk.CONTROL_MASK
+ ):
+ if self._isFullScreen:
+ self._window.unfullscreen()
+ else:
+ self._window.fullscreen()
+ elif event.keyval == ord("c") and event.get_state() & gtk.gdk.CONTROL_MASK:
message = "\n".join(
messagePart[0]
for messagePart in self._messagemodel
)
self._clipboard.set_text(str(message))
+ elif (
+ event.keyval == gtk.keysyms.h and
+ event.get_state() & gtk.gdk.CONTROL_MASK
+ ):
+ self._window.hide()
+ elif (
+ event.keyval == gtk.keysyms.w and
+ event.get_state() & gtk.gdk.CONTROL_MASK
+ ):
+ self._pseudo_destroy()
+ elif (
+ event.keyval == gtk.keysyms.q and
+ event.get_state() & gtk.gdk.CONTROL_MASK
+ ):
+ self._parent.destroy()
except Exception, e:
- _moduleLogger.exception(str(e))
+ self._errorDisplay.push_exception()
class Dialpad(object):
self._errorDisplay = errorDisplay
self._numberdisplay = widgetTree.get_widget("numberdisplay")
- self._okButton = widgetTree.get_widget("dialpadOk")
+ self._callButton = widgetTree.get_widget("dialpadCall")
+ self._sendSMSButton = widgetTree.get_widget("dialpadSMS")
self._backButton = widgetTree.get_widget("back")
self._plusButton = widgetTree.get_widget("plus")
self._phonenumber = ""
"on_digit_clicked": self._on_digit_clicked,
}
widgetTree.signal_autoconnect(callbackMapping)
- self._okButton.connect("clicked", self._on_ok_clicked)
+ self._sendSMSButton.connect("clicked", self._on_sms_clicked)
+ self._callButton.connect("clicked", self._on_call_clicked)
self._plusButton.connect("clicked", self._on_plus)
self._originalLabel = self._backButton.get_label()
self._keyPressEventId = 0
def enable(self):
- self._okButton.grab_focus()
+ self._sendSMSButton.grab_focus()
self._backTapHandler.enable()
self._keyPressEventId = self._window.connect("key-press-event", self._on_key_press)
def add_contact(self, *args, **kwds):
"""
- @note Actual dial function is patched in later
+ @note Actual function is patched in later
+ """
+ raise NotImplementedError("Horrible unknown error has occurred")
+
+ def dial(self, number):
+ """
+ @note Actual function is patched in later
"""
raise NotImplementedError("Horrible unknown error has occurred")
self._phonenumber = make_ugly(number)
self._prettynumber = make_pretty(self._phonenumber)
self._numberdisplay.set_label("<span size='30000' weight='bold'>%s</span>" % (self._prettynumber))
+ if self._phonenumber:
+ self._plusButton.set_sensitive(False)
+ else:
+ self._plusButton.set_sensitive(True)
except TypeError, e:
self._errorDisplay.push_exception()
"""
pass
+ def set_orientation(self, orientation):
+ if orientation == gtk.ORIENTATION_VERTICAL:
+ pass
+ elif orientation == gtk.ORIENTATION_HORIZONTAL:
+ pass
+ else:
+ raise NotImplementedError(orientation)
+
def _on_key_press(self, widget, event):
try:
if event.keyval == ord("v") and event.get_state() & gtk.gdk.CONTROL_MASK:
except Exception, e:
self._errorDisplay.push_exception()
- def _on_ok_clicked(self, widget):
+ def _on_call_clicked(self, widget):
+ try:
+ phoneNumber = self.get_number()
+ self.dial(phoneNumber)
+ self.set_number("")
+ except Exception, e:
+ self._errorDisplay.push_exception()
+
+ def _on_sms_clicked(self, widget):
try:
phoneNumber = self.get_number()
self.add_contact(
- [("Dialer", phoneNumber)], (), self._window
+ "(Dialpad)",
+ [("Dialer", phoneNumber)], ()
)
+ self.set_number("")
except Exception, e:
self._errorDisplay.push_exception()
config.set(section, "notifyOnVoicemail", repr(self._notifyOnVoicemail))
config.set(section, "notifyOnSms", repr(self._notifyOnSms))
+ def set_orientation(self, orientation):
+ if orientation == gtk.ORIENTATION_VERTICAL:
+ pass
+ elif orientation == gtk.ORIENTATION_HORIZONTAL:
+ pass
+ else:
+ raise NotImplementedError(orientation)
+
def _populate_callback_combo(self):
self._isPopulated = True
del self._callbackList[:]
"""
config.set(sectionName, "filter", self._selectedFilter)
+ def set_orientation(self, orientation):
+ if orientation == gtk.ORIENTATION_VERTICAL:
+ pass
+ elif orientation == gtk.ORIENTATION_HORIZONTAL:
+ pass
+ else:
+ raise NotImplementedError(orientation)
+
def _is_history_visible(self, model, iter):
try:
action = model.get_value(iter, self.ACTION_IDX)
except Exception, e:
self._errorDisplay.push_exception()
+ def _history_summary(self, expectedNumber):
+ for number, action, date, whoFrom, whoFromId in self._historymodel:
+ if expectedNumber is not None and expectedNumber == number:
+ yield "%s <i>(%s)</i> - %s %s" % (number, whoFrom, date, action)
+
def _on_historyview_row_activated(self, treeview, path, view_column):
try:
childPath = self._historymodelfiltered.convert_path_to_child_path(path)
if not itr:
return
- number = self._historymodel.get_value(itr, self.NUMBER_IDX)
- number = make_ugly(number)
- description = self._historymodel.get_value(itr, self.FROM_IDX)
+ prettyNumber = self._historymodel.get_value(itr, self.NUMBER_IDX)
+ number = make_ugly(prettyNumber)
+ description = list(self._history_summary(prettyNumber))
+ contactName = self._historymodel.get_value(itr, self.FROM_IDX)
contactId = self._historymodel.get_value(itr, self.FROM_ID_IDX)
- if contactId:
- contactPhoneNumbers = list(self._backend.get_contact_details(contactId))
- defaultMatches = [
- (number == make_ugly(contactNumber) or number[1:] == make_ugly(contactNumber))
- for (numberDescription, contactNumber) in contactPhoneNumbers
- ]
- try:
- defaultIndex = defaultMatches.index(True)
- except ValueError:
- contactPhoneNumbers.append(("Other", number))
- defaultIndex = len(contactPhoneNumbers)-1
- _moduleLogger.warn(
- "Could not find contact %r's number %s among %r" % (
- contactId, number, contactPhoneNumbers
- )
- )
- else:
- contactPhoneNumbers = [("Phone", number)]
- defaultIndex = -1
+ contactPhoneNumbers, defaultIndex = _get_contact_numbers(self._backend, contactId, number)
self.add_contact(
+ contactName,
contactPhoneNumbers,
- messages = (description, ),
- parent = self._window,
+ messages = description,
defaultIndex = defaultIndex,
)
self._historyviewselection.unselect_all()
NO_MESSAGES = "None"
VOICEMAIL_MESSAGES = "Voicemail"
- TEXT_MESSAGES = "Texts"
+ TEXT_MESSAGES = "SMS"
ALL_TYPES = "All Messages"
MESSAGE_TYPES = [NO_MESSAGES, VOICEMAIL_MESSAGES, TEXT_MESSAGES, ALL_TYPES]
config.set(sectionName, "status", self._messageStatus)
config.set(sectionName, "type", self._messageType)
+ def set_orientation(self, orientation):
+ if orientation == gtk.ORIENTATION_VERTICAL:
+ pass
+ elif orientation == gtk.ORIENTATION_HORIZONTAL:
+ pass
+ else:
+ raise NotImplementedError(orientation)
+
def _is_message_visible(self, model, iter):
try:
message = model.get_value(iter, self.MESSAGE_DATA_IDX)
description = self._messagemodel.get_value(itr, self.MESSAGES_IDX)
contactId = self._messagemodel.get_value(itr, self.FROM_ID_IDX)
- if contactId:
- contactPhoneNumbers = list(self._backend.get_contact_details(contactId))
- defaultMatches = [
- (number == make_ugly(contactNumber) or number[1:] == make_ugly(contactNumber))
- for (numberDescription, contactNumber) in contactPhoneNumbers
- ]
- try:
- defaultIndex = defaultMatches.index(True)
- except ValueError:
- contactPhoneNumbers.append(("Other", number))
- defaultIndex = len(contactPhoneNumbers)-1
- _moduleLogger.warn(
- "Could not find contact %r's number %s among %r" % (
- contactId, number, contactPhoneNumbers
- )
- )
- else:
- contactPhoneNumbers = [("Phone", number)]
- defaultIndex = -1
+ header = self._messagemodel.get_value(itr, self.HEADER_IDX)
+ contactPhoneNumbers, defaultIndex = _get_contact_numbers(self._backend, contactId, number)
self.add_contact(
+ header,
contactPhoneNumbers,
messages = description,
- parent = self._window,
defaultIndex = defaultIndex,
)
self._messageviewselection.unselect_all()
def save_settings(self, config, sectionName):
config.set(sectionName, "selectedAddressbook", str(self._selectedComboIndex))
+ def set_orientation(self, orientation):
+ if orientation == gtk.ORIENTATION_VERTICAL:
+ pass
+ elif orientation == gtk.ORIENTATION_HORIZONTAL:
+ pass
+ else:
+ raise NotImplementedError(orientation)
+
def _idly_populate_contactsview(self):
with gtk_toolbox.gtk_lock():
banner = hildonize.show_busy_banner_start(self._window, "Loading Contacts")
return
self.add_contact(
+ contactName,
contactPhoneNumbers,
messages = (contactName, ),
- parent = self._window,
)
self._contactsviewselection.unselect_all()
except Exception, e: