X-Git-Url: https://vcs.maemo.org/git/?a=blobdiff_plain;f=src%2Fgv_views.py;h=f57995ce2848c65ef85d179773a4f1e9f3bccd05;hb=9708c3b670d58c0370a45e28dcbd0abc9d8276a8;hp=56f11f73b5967395b8c80f10d41f5e937f89063d;hpb=5adbb1c1657c53228509c40c59f461df2b139019;p=gc-dialer diff --git a/src/gv_views.py b/src/gv_views.py index 56f11f7..f57995c 100644 --- a/src/gv_views.py +++ b/src/gv_views.py @@ -60,9 +60,19 @@ class Dialpad(object): smsIcon = self._app.get_icon("messages.png") self._smsButton = QtGui.QPushButton(smsIcon, "SMS") self._smsButton.clicked.connect(self._on_sms_clicked) + self._smsButton.setSizePolicy(QtGui.QSizePolicy( + QtGui.QSizePolicy.MinimumExpanding, + QtGui.QSizePolicy.MinimumExpanding, + QtGui.QSizePolicy.PushButton, + )) callIcon = self._app.get_icon("dialpad.png") self._callButton = QtGui.QPushButton(callIcon, "Call") self._callButton.clicked.connect(self._on_call_clicked) + self._callButton.setSizePolicy(QtGui.QSizePolicy( + QtGui.QSizePolicy.MinimumExpanding, + QtGui.QSizePolicy.MinimumExpanding, + QtGui.QSizePolicy.PushButton, + )) self._padLayout = QtGui.QGridLayout() rows = [0, 0, 0, 1, 1, 1, 2, 2, 2] @@ -82,13 +92,18 @@ class Dialpad(object): self._padLayout.addWidget(self._generate_key_button(num, letters), row, column) self._zerothButton = QtGui.QPushButton("0") self._zerothButton.clicked.connect(lambda: self._on_keypress("0")) + self._zerothButton.setSizePolicy(QtGui.QSizePolicy( + QtGui.QSizePolicy.MinimumExpanding, + QtGui.QSizePolicy.MinimumExpanding, + QtGui.QSizePolicy.PushButton, + )) self._padLayout.addWidget(self._smsButton, 3, 0) self._padLayout.addWidget(self._zerothButton) self._padLayout.addWidget(self._callButton, 3, 2) self._layout = QtGui.QVBoxLayout() - self._layout.addLayout(self._entryLayout) - self._layout.addLayout(self._padLayout) + self._layout.addLayout(self._entryLayout, 0) + self._layout.addLayout(self._padLayout, 1000000) self._widget = QtGui.QWidget() self._widget.setLayout(self._layout) @@ -118,48 +133,58 @@ class Dialpad(object): def _generate_key_button(self, center, letters): button = QtGui.QPushButton("%s\n%s" % (center, letters)) + button.setSizePolicy(QtGui.QSizePolicy( + QtGui.QSizePolicy.MinimumExpanding, + QtGui.QSizePolicy.MinimumExpanding, + QtGui.QSizePolicy.PushButton, + )) button.clicked.connect(lambda: self._on_keypress(center)) return button @misc_utils.log_exception(_moduleLogger) def _on_keypress(self, key): - self._entry.insert(key) + with qui_utils.notify_error(self._errorLog): + self._entry.insert(key) @misc_utils.log_exception(_moduleLogger) def _on_backspace(self, toggled = False): - self._entry.backspace() + with qui_utils.notify_error(self._errorLog): + self._entry.backspace() @misc_utils.log_exception(_moduleLogger) def _on_clear_text(self, toggled = False): - self._entry.clear() + with qui_utils.notify_error(self._errorLog): + self._entry.clear() @QtCore.pyqtSlot() @QtCore.pyqtSlot(bool) @misc_utils.log_exception(_moduleLogger) def _on_sms_clicked(self, checked = False): - number = misc_utils.make_ugly(str(self._entry.text())) - self._entry.clear() + with qui_utils.notify_error(self._errorLog): + number = misc_utils.make_ugly(str(self._entry.text())) + self._entry.clear() - contactId = number - title = misc_utils.make_pretty(number) - description = misc_utils.make_pretty(number) - numbersWithDescriptions = [(number, "")] - self._session.draft.add_contact(contactId, title, description, numbersWithDescriptions) + contactId = number + title = misc_utils.make_pretty(number) + description = misc_utils.make_pretty(number) + numbersWithDescriptions = [(number, "")] + self._session.draft.add_contact(contactId, None, title, description, numbersWithDescriptions) @QtCore.pyqtSlot() @QtCore.pyqtSlot(bool) @misc_utils.log_exception(_moduleLogger) def _on_call_clicked(self, checked = False): - number = misc_utils.make_ugly(str(self._entry.text())) - self._entry.clear() + with qui_utils.notify_error(self._errorLog): + number = misc_utils.make_ugly(str(self._entry.text())) + self._entry.clear() - contactId = number - title = misc_utils.make_pretty(number) - description = misc_utils.make_pretty(number) - numbersWithDescriptions = [(number, "")] - self._session.draft.clear() - self._session.draft.add_contact(contactId, title, description, numbersWithDescriptions) - self._session.draft.call() + contactId = number + title = misc_utils.make_pretty(number) + description = misc_utils.make_pretty(number) + numbersWithDescriptions = [(number, "")] + self._session.draft.clear() + self._session.draft.add_contact(contactId, None, title, description, numbersWithDescriptions) + self._session.draft.call() class TimeCategories(object): @@ -197,8 +222,15 @@ class TimeCategories(object): self._today = newToday for item in self._timeItems: item.removeRows(0, item.rowCount()) - self._timeItems[self._NOW_SECTION].setText(self._today.strftime("%X")) - self._timeItems[self._TODAY_SECTION].setText(self._today.strftime("%x")) + try: + hour = self._today.strftime("%X") + day = self._today.strftime("%x") + except ValueError: + _moduleLogger.exception("Can't format times") + hour = "Now" + day = "Today" + self._timeItems[self._NOW_SECTION].setText(hour) + self._timeItems[self._TODAY_SECTION].setText(day) def add_row(self, rowDate, row): elapsedTime = self._today - rowDate @@ -216,6 +248,11 @@ class TimeCategories(object): section = self._REST_SECTION self._timeItems[section].appendRow(row) + def get_item(self, timeIndex, rowIndex, column): + timeItem = self._timeItems[timeIndex] + item = timeItem.child(rowIndex, column) + return item + class History(object): @@ -223,7 +260,12 @@ class History(object): FROM_IDX = 1 MAX_IDX = 2 - HISTORY_ITEM_TYPES = ["Received", "Missed", "Placed", "All"] + HISTORY_RECEIVED = "Received" + HISTORY_MISSED = "Missed" + HISTORY_PLACED = "Placed" + HISTORY_ALL = "All" + + HISTORY_ITEM_TYPES = [HISTORY_RECEIVED, HISTORY_MISSED, HISTORY_PLACED, HISTORY_ALL] HISTORY_COLUMNS = ["Details", "From"] assert len(HISTORY_COLUMNS) == MAX_IDX @@ -240,6 +282,19 @@ class History(object): self.HISTORY_ITEM_TYPES.index(self._selectedFilter) ) self._typeSelection.currentIndexChanged[str].connect(self._on_filter_changed) + refreshIcon = qui_utils.get_theme_icon( + ("view-refresh", "general_refresh", "gtk-refresh", ) + ) + self._refreshButton = QtGui.QPushButton(refreshIcon, "") + self._refreshButton.clicked.connect(self._on_refresh_clicked) + self._refreshButton.setSizePolicy(QtGui.QSizePolicy( + QtGui.QSizePolicy.Minimum, + QtGui.QSizePolicy.Minimum, + QtGui.QSizePolicy.PushButton, + )) + self._managerLayout = QtGui.QHBoxLayout() + self._managerLayout.addWidget(self._typeSelection, 1000) + self._managerLayout.addWidget(self._refreshButton, 0) self._itemStore = QtGui.QStandardItemModel() self._itemStore.setHorizontalHeaderLabels(self.HISTORY_COLUMNS) @@ -253,11 +308,12 @@ class History(object): self._itemView.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) self._itemView.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) self._itemView.setHeaderHidden(True) + self._itemView.setItemsExpandable(False) self._itemView.header().setResizeMode(QtGui.QHeaderView.ResizeToContents) self._itemView.activated.connect(self._on_row_activated) self._layout = QtGui.QVBoxLayout() - self._layout.addWidget(self._typeSelection) + self._layout.addLayout(self._managerLayout) self._layout.addWidget(self._itemView) self._widget = QtGui.QWidget() self._widget.setLayout(self._layout) @@ -291,7 +347,21 @@ class History(object): self._itemView.clear() def refresh(self, force=True): - self._session.update_history(force) + self._itemView.setFocus(QtCore.Qt.OtherFocusReason) + + if self._selectedFilter == self.HISTORY_RECEIVED: + self._session.update_history(self._session.HISTORY_RECEIVED, force) + elif self._selectedFilter == self.HISTORY_MISSED: + self._session.update_history(self._session.HISTORY_MISSED, force) + elif self._selectedFilter == self.HISTORY_PLACED: + self._session.update_history(self._session.HISTORY_PLACED, force) + elif self._selectedFilter == self.HISTORY_ALL: + self._session.update_history(self._session.HISTORY_ALL, force) + else: + assert False, "How did we get here?" + + if self._app.notifyOnMissed and self._app.alarmHandler.alarmType == self._app.alarmHandler.ALARM_BACKGROUND: + self._app.ledHandler.off() def _populate_items(self): self._categoryManager.prepare_for_update(self._session.get_when_history_updated()) @@ -331,41 +401,57 @@ class History(object): @QtCore.pyqtSlot(str) @misc_utils.log_exception(_moduleLogger) def _on_filter_changed(self, newItem): - self._selectedFilter = str(newItem) - self._populate_items() + with qui_utils.notify_error(self._errorLog): + self._selectedFilter = str(newItem) + self._populate_items() @QtCore.pyqtSlot() @misc_utils.log_exception(_moduleLogger) def _on_history_updated(self): - self._populate_items() + with qui_utils.notify_error(self._errorLog): + self._populate_items() + + @QtCore.pyqtSlot() + @misc_utils.log_exception(_moduleLogger) + def _on_refresh_clicked(self, arg = None): + with qui_utils.notify_error(self._errorLog): + self.refresh(force=True) @QtCore.pyqtSlot(QtCore.QModelIndex) @misc_utils.log_exception(_moduleLogger) def _on_row_activated(self, index): - rowIndex = index.row() - item = self._itemStore.item(rowIndex, 0) - contactDetails = item.data().toPyObject() - - title = str(self._itemStore.item(rowIndex, self.FROM_IDX).text()) - number = str(contactDetails[QtCore.QString("number")]) - contactId = number # ids don't seem too unique so using numbers - - descriptionRows = [] - for i in xrange(self._itemStore.rowCount()): - iItem = self._itemStore.item(i, 0) - iContactDetails = iItem.data().toPyObject() - iNumber = str(iContactDetails[QtCore.QString("number")]) - if number != iNumber: - continue - relTime = misc_utils.abbrev_relative_date(iContactDetails[QtCore.QString("relTime")]) - action = str(iContactDetails[QtCore.QString("action")]) - number = str(iContactDetails[QtCore.QString("number")]) - prettyNumber = misc_utils.make_pretty(number) - rowItems = relTime, action, prettyNumber - descriptionRows.append("%s" % "".join(rowItems)) - description = "%s
" % "".join(descriptionRows) - numbersWithDescriptions = [(str(contactDetails[QtCore.QString("number")]), "")] - self._session.draft.add_contact(contactId, title, description, numbersWithDescriptions) + with qui_utils.notify_error(self._errorLog): + timeIndex = index.parent() + if not timeIndex.isValid(): + return + timeRow = timeIndex.row() + row = index.row() + detailsItem = self._categoryManager.get_item(timeRow, row, self.DETAILS_IDX) + fromItem = self._categoryManager.get_item(timeRow, row, self.FROM_IDX) + contactDetails = detailsItem.data().toPyObject() + + title = unicode(fromItem.text()) + number = str(contactDetails[QtCore.QString("number")]) + contactId = number # ids don't seem too unique so using numbers + + descriptionRows = [] + for t in xrange(self._itemStore.rowCount()): + randomTimeItem = self._itemStore.item(t, 0) + for i in xrange(randomTimeItem.rowCount()): + iItem = randomTimeItem.child(i, 0) + iContactDetails = iItem.data().toPyObject() + iNumber = str(iContactDetails[QtCore.QString("number")]) + if number != iNumber: + continue + relTime = misc_utils.abbrev_relative_date(iContactDetails[QtCore.QString("relTime")]) + action = str(iContactDetails[QtCore.QString("action")]) + number = str(iContactDetails[QtCore.QString("number")]) + prettyNumber = misc_utils.make_pretty(number) + rowItems = relTime, action, prettyNumber + descriptionRows.append("%s" % "".join(rowItems)) + description = "%s
" % "".join(descriptionRows) + numbersWithDescriptions = [(str(contactDetails[QtCore.QString("number")]), "")] + self._session.draft.add_contact(contactId, None, title, description, numbersWithDescriptions) class Messages(object): @@ -381,7 +467,7 @@ class Messages(object): ALL_STATUS = "Any" MESSAGE_STATUSES = [UNREAD_STATUS, UNARCHIVED_STATUS, ALL_STATUS] - _MIN_MESSAGES_SHOWN = 4 + _MIN_MESSAGES_SHOWN = 1 def __init__(self, app, session, errorLog): self._selectedTypeFilter = self.ALL_TYPES @@ -405,9 +491,21 @@ class Messages(object): ) self._statusSelection.currentIndexChanged[str].connect(self._on_status_filter_changed) + refreshIcon = qui_utils.get_theme_icon( + ("view-refresh", "general_refresh", "gtk-refresh", ) + ) + self._refreshButton = QtGui.QPushButton(refreshIcon, "") + self._refreshButton.clicked.connect(self._on_refresh_clicked) + self._refreshButton.setSizePolicy(QtGui.QSizePolicy( + QtGui.QSizePolicy.Minimum, + QtGui.QSizePolicy.Minimum, + QtGui.QSizePolicy.PushButton, + )) + self._selectionLayout = QtGui.QHBoxLayout() - self._selectionLayout.addWidget(self._typeSelection) - self._selectionLayout.addWidget(self._statusSelection) + self._selectionLayout.addWidget(self._typeSelection, 1000) + self._selectionLayout.addWidget(self._statusSelection, 1000) + self._selectionLayout.addWidget(self._refreshButton, 0) self._itemStore = QtGui.QStandardItemModel() self._itemStore.setHorizontalHeaderLabels(["Messages"]) @@ -422,8 +520,10 @@ class Messages(object): self._itemView.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) self._itemView.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) self._itemView.setHeaderHidden(True) + self._itemView.setItemsExpandable(False) self._itemView.setItemDelegate(self._htmlDelegate) self._itemView.activated.connect(self._on_row_activated) + self._itemView.header().sectionResized.connect(self._on_column_resized) self._layout = QtGui.QVBoxLayout() self._layout.addLayout(self._selectionLayout) @@ -468,7 +568,21 @@ class Messages(object): self._itemView.clear() def refresh(self, force=True): - self._session.update_messages(force) + self._itemView.setFocus(QtCore.Qt.OtherFocusReason) + + if self._selectedTypeFilter == self.NO_MESSAGES: + pass + elif self._selectedTypeFilter == self.TEXT_MESSAGES: + self._session.update_messages(self._session.MESSAGE_TEXTS, force) + elif self._selectedTypeFilter == self.VOICEMAIL_MESSAGES: + self._session.update_messages(self._session.MESSAGE_VOICEMAILS, force) + elif self._selectedTypeFilter == self.ALL_TYPES: + self._session.update_messages(self._session.MESSAGE_ALL, force) + else: + assert False, "How did we get here?" + + if self._app.notifyOnSms or self._app.notifyOnVoicemail and self._app.alarmHandler.alarmType == self._app.alarmHandler.ALARM_BACKGROUND: + self._app.ledHandler.off() def _populate_items(self): self._categoryManager.prepare_for_update(self._session.get_when_messages_updated()) @@ -510,11 +624,11 @@ class Messages(object): for messagePart in messageParts ] - firstMessage = "%s - %s (%s)" % (name, prettyNumber, relTime) + firstMessage = "%s
%s
(%s)" % (name, prettyNumber, relTime) expandedMessages = [firstMessage] expandedMessages.extend(messages) - if (self._MIN_MESSAGES_SHOWN + 1) < len(messages): + if self._MIN_MESSAGES_SHOWN < len(messages): secondMessage = "%d Messages Hidden..." % (len(messages) - self._MIN_MESSAGES_SHOWN, ) collapsedMessages = [firstMessage, secondMessage] collapsedMessages.extend(messages[-(self._MIN_MESSAGES_SHOWN+0):]) @@ -536,39 +650,62 @@ class Messages(object): @QtCore.pyqtSlot(str) @misc_utils.log_exception(_moduleLogger) def _on_type_filter_changed(self, newItem): - self._selectedTypeFilter = str(newItem) - self._populate_items() + with qui_utils.notify_error(self._errorLog): + self._selectedTypeFilter = str(newItem) + self._populate_items() @QtCore.pyqtSlot(str) @misc_utils.log_exception(_moduleLogger) def _on_status_filter_changed(self, newItem): - self._selectedStatusFilter = str(newItem) - self._populate_items() + with qui_utils.notify_error(self._errorLog): + self._selectedStatusFilter = str(newItem) + self._populate_items() + + @QtCore.pyqtSlot() + @misc_utils.log_exception(_moduleLogger) + def _on_refresh_clicked(self, arg = None): + with qui_utils.notify_error(self._errorLog): + self.refresh(force=True) @QtCore.pyqtSlot() @misc_utils.log_exception(_moduleLogger) def _on_messages_updated(self): - self._populate_items() + with qui_utils.notify_error(self._errorLog): + self._populate_items() @QtCore.pyqtSlot(QtCore.QModelIndex) @misc_utils.log_exception(_moduleLogger) def _on_row_activated(self, index): - rowIndex = index.row() - item = self._itemStore.item(rowIndex, 0) - contactDetails = item.data().toPyObject() + with qui_utils.notify_error(self._errorLog): + timeIndex = index.parent() + if not timeIndex.isValid(): + return + timeRow = timeIndex.row() + row = index.row() + item = self._categoryManager.get_item(timeRow, row, 0) + contactDetails = item.data().toPyObject() + + name = unicode(contactDetails[QtCore.QString("name")]) + number = str(contactDetails[QtCore.QString("number")]) + if not name or name == number: + name = unicode(contactDetails[QtCore.QString("location")]) + if not name: + name = "Unknown" - name = str(contactDetails[QtCore.QString("name")]) - number = str(contactDetails[QtCore.QString("number")]) - if not name or name == number: - name = str(contactDetails[QtCore.QString("location")]) - if not name: - name = "Unknown" + if str(contactDetails[QtCore.QString("type")]) == "Voicemail": + messageId = str(contactDetails[QtCore.QString("id")]) + else: + messageId = None + contactId = str(contactDetails[QtCore.QString("contactId")]) + title = name + description = unicode(contactDetails[QtCore.QString("expandedMessages")]) + numbersWithDescriptions = [(number, "")] + self._session.draft.add_contact(contactId, messageId, title, description, numbersWithDescriptions) - contactId = str(contactDetails[QtCore.QString("id")]) - title = name - description = str(contactDetails[QtCore.QString("expandedMessages")]) - numbersWithDescriptions = [(number, "")] - self._session.draft.add_contact(contactId, title, description, numbersWithDescriptions) + @QtCore.pyqtSlot(QtCore.QModelIndex) + @misc_utils.log_exception(_moduleLogger) + def _on_column_resized(self, index, oldSize, newSize): + self._htmlDelegate.setWidth(newSize, self._itemStore) class Contacts(object): @@ -578,7 +715,7 @@ class Contacts(object): def __init__(self, app, session, errorLog): self._app = app self._session = session - self._session.contactsUpdated.connect(self._on_contacts_updated) + self._session.accountUpdated.connect(self._on_contacts_updated) self._errorLog = errorLog self._addressBookFactories = [ null_backend.NullAddressBookFactory(), @@ -590,6 +727,19 @@ class Contacts(object): self._listSelection.addItems([]) self._listSelection.currentIndexChanged[str].connect(self._on_filter_changed) self._activeList = "None" + refreshIcon = qui_utils.get_theme_icon( + ("view-refresh", "general_refresh", "gtk-refresh", ) + ) + self._refreshButton = QtGui.QPushButton(refreshIcon, "") + self._refreshButton.clicked.connect(self._on_refresh_clicked) + self._refreshButton.setSizePolicy(QtGui.QSizePolicy( + QtGui.QSizePolicy.Minimum, + QtGui.QSizePolicy.Minimum, + QtGui.QSizePolicy.PushButton, + )) + self._managerLayout = QtGui.QHBoxLayout() + self._managerLayout.addWidget(self._listSelection, 1000) + self._managerLayout.addWidget(self._refreshButton, 0) self._itemStore = QtGui.QStandardItemModel() self._itemStore.setHorizontalHeaderLabels(["Contacts"]) @@ -607,7 +757,7 @@ class Contacts(object): self._itemView.activated.connect(self._on_row_activated) self._layout = QtGui.QVBoxLayout() - self._layout.addWidget(self._listSelection) + self._layout.addLayout(self._managerLayout) self._layout.addWidget(self._itemView) self._widget = QtGui.QWidget() self._widget.setLayout(self._layout) @@ -645,7 +795,8 @@ class Contacts(object): self._itemView.clear() def refresh(self, force=True): - self._backend.update_contacts(force) + self._itemView.setFocus(QtCore.Qt.OtherFocusReason) + self._backend.update_account(force) @property def _backend(self): @@ -732,52 +883,62 @@ class Contacts(object): @QtCore.pyqtSlot(str) @misc_utils.log_exception(_moduleLogger) def _on_filter_changed(self, newItem): - self._activeList = str(newItem) - self.refresh(force=False) - self._populate_items() + with qui_utils.notify_error(self._errorLog): + self._activeList = str(newItem) + self.refresh(force=False) + self._populate_items() + + @QtCore.pyqtSlot() + @misc_utils.log_exception(_moduleLogger) + def _on_refresh_clicked(self, arg = None): + with qui_utils.notify_error(self._errorLog): + self.refresh(force=True) @QtCore.pyqtSlot() @misc_utils.log_exception(_moduleLogger) def _on_contacts_updated(self): - self._populate_items() + with qui_utils.notify_error(self._errorLog): + self._populate_items() @QtCore.pyqtSlot(QtCore.QModelIndex) @misc_utils.log_exception(_moduleLogger) def _on_row_activated(self, index): - letterIndex = index.parent() - assert letterIndex.isValid() - letterRow = letterIndex.row() - letter = list(self._prefixes())[letterRow] - letterItem = self._alphaItem[letter] - rowIndex = index.row() - item = letterItem.child(rowIndex, 0) - contactDetails = item.data().toPyObject() - - name = str(contactDetails[QtCore.QString("name")]) - if not name: - name = str(contactDetails[QtCore.QString("location")]) - if not name: - name = "Unknown" - - contactId = str(contactDetails[QtCore.QString("contactId")]) - numbers = contactDetails[QtCore.QString("numbers")] - numbers = [ - dict( - (str(k), str(v)) - for (k, v) in number.iteritems() - ) - for number in numbers - ] - numbersWithDescriptions = [ - ( - number["phoneNumber"], - self._choose_phonetype(number), - ) - for number in numbers - ] - title = name - description = name - self._session.draft.add_contact(contactId, title, description, numbersWithDescriptions) + with qui_utils.notify_error(self._errorLog): + letterIndex = index.parent() + if not letterIndex.isValid(): + return + letterRow = letterIndex.row() + letter = list(self._prefixes())[letterRow] + letterItem = self._alphaItem[letter] + rowIndex = index.row() + item = letterItem.child(rowIndex, 0) + contactDetails = item.data().toPyObject() + + name = unicode(contactDetails[QtCore.QString("name")]) + if not name: + name = unicode(contactDetails[QtCore.QString("location")]) + if not name: + name = "Unknown" + + contactId = str(contactDetails[QtCore.QString("contactId")]) + numbers = contactDetails[QtCore.QString("numbers")] + numbers = [ + dict( + (str(k), str(v)) + for (k, v) in number.iteritems() + ) + for number in numbers + ] + numbersWithDescriptions = [ + ( + number["phoneNumber"], + self._choose_phonetype(number), + ) + for number in numbers + ] + title = name + description = name + self._session.draft.add_contact(contactId, None, title, description, numbersWithDescriptions) @staticmethod def _choose_phonetype(numberDetails):