4 DialCentral - Front end for Google's GoogleVoice service.
5 Copyright (C) 2008 Mark Bergman bergman AT merctech DOT com
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 from __future__ import with_statement
42 _moduleLogger = logging.getLogger("dc_glade")
43 PROFILE_STARTUP = False
46 def getmtime_nothrow(path):
48 return os.path.getmtime(path)
53 def display_error_message(msg):
54 error_dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, msg)
56 def close(dialog, response):
58 error_dialog.connect("response", close)
62 class Dialcentral(object):
65 os.path.join(os.path.dirname(__file__), "dialcentral.glade"),
66 os.path.join(os.path.dirname(__file__), "../lib/dialcentral.glade"),
67 '/usr/lib/dialcentral/dialcentral.glade',
77 # 1 Was GrandCentral support so the gap was maintained for compatibility
79 BACKENDS = (NULL_BACKEND, GV_BACKEND)
82 self._initDone = False
83 self._connection = None
85 self._deviceState = None
86 self._clipboard = gtk.clipboard_get()
88 self._credentials = ("", "")
89 self._selectedBackendId = self.NULL_BACKEND
90 self._defaultBackendId = self.GV_BACKEND
91 self._phoneBackends = None
93 self._accountViews = None
94 self._messagesViews = None
95 self._recentViews = None
96 self._contactsViews = None
97 self._alarmHandler = None
98 self._ledHandler = None
99 self._originalCurrentLabels = []
101 for path in self._glade_files:
102 if os.path.isfile(path):
103 self._widgetTree = gtk.glade.XML(path)
106 display_error_message("Cannot find dialcentral.glade")
110 self._window = self._widgetTree.get_widget("mainWindow")
111 self._notebook = self._widgetTree.get_widget("notebook")
112 self._errorDisplay = gtk_toolbox.ErrorDisplay(self._widgetTree)
113 self._credentialsDialog = gtk_toolbox.LoginWindow(self._widgetTree)
115 self._isFullScreen = False
116 self._app = hildonize.get_app_class()()
117 self._window = hildonize.hildonize_window(self._app, self._window)
118 hildonize.hildonize_text_entry(self._widgetTree.get_widget("usernameentry"))
119 hildonize.hildonize_password_entry(self._widgetTree.get_widget("passwordentry"))
121 for scrollingWidgetName in (
122 'recent_scrolledwindow',
123 'message_scrolledwindow',
124 'contacts_scrolledwindow',
125 "smsMessages_scrolledwindow",
127 scrollingWidget = self._widgetTree.get_widget(scrollingWidgetName)
128 assert scrollingWidget is not None, scrollingWidgetName
129 hildonize.hildonize_scrollwindow(scrollingWidget)
130 for scrollingWidgetName in (
131 "smsMessage_scrolledEntry",
133 scrollingWidget = self._widgetTree.get_widget(scrollingWidgetName)
134 assert scrollingWidget is not None, scrollingWidgetName
135 hildonize.hildonize_scrollwindow_with_viewport(scrollingWidget)
139 "addressbookSelectButton",
143 "callbackSelectButton",
144 "minutesEntryButton",
146 "phoneTypeSelection",
148 button = self._widgetTree.get_widget(buttonName)
149 assert button is not None, buttonName
150 hildonize.set_button_thumb_selectable(button)
152 replacementButtons = [gtk.Button("Test")]
153 menu = hildonize.hildonize_menu(
155 self._widgetTree.get_widget("dialpad_menubar"),
159 self._window.connect("key-press-event", self._on_key_press)
160 self._window.connect("window-state-event", self._on_window_state_change)
161 if not hildonize.IS_HILDON_SUPPORTED:
162 _moduleLogger.warning("No hildonization support")
164 hildonize.set_application_title(self._window, "%s" % constants.__pretty_app_name__)
166 self._window.connect("destroy", self._on_close)
167 self._window.set_default_size(800, 300)
168 self._window.show_all()
170 self._loginSink = gtk_toolbox.threaded_stage(
173 gtk_toolbox.null_sink(),
177 if not PROFILE_STARTUP:
178 backgroundSetup = threading.Thread(target=self._idle_setup)
179 backgroundSetup.setDaemon(True)
180 backgroundSetup.start()
184 def _idle_setup(self):
186 If something can be done after the UI loads, push it here so it's not blocking the UI
188 # Barebones UI handlers
190 from backends import null_backend
193 self._phoneBackends = {self.NULL_BACKEND: null_backend.NullDialer()}
194 with gtk_toolbox.gtk_lock():
195 self._dialpads = {self.NULL_BACKEND: null_views.Dialpad(self._widgetTree)}
196 self._accountViews = {self.NULL_BACKEND: null_views.AccountInfo(self._widgetTree)}
197 self._recentViews = {self.NULL_BACKEND: null_views.RecentCallsView(self._widgetTree)}
198 self._messagesViews = {self.NULL_BACKEND: null_views.MessagesView(self._widgetTree)}
199 self._contactsViews = {self.NULL_BACKEND: null_views.ContactsView(self._widgetTree)}
201 self._dialpads[self._selectedBackendId].enable()
202 self._accountViews[self._selectedBackendId].enable()
203 self._recentViews[self._selectedBackendId].enable()
204 self._messagesViews[self._selectedBackendId].enable()
205 self._contactsViews[self._selectedBackendId].enable()
207 with gtk_toolbox.gtk_lock():
208 self._errorDisplay.push_exception()
210 # Setup maemo specifics
214 except (ImportError, OSError):
217 self._deviceState = None
219 self._osso = osso.Context(constants.__app_name__, constants.__version__, False)
220 self._deviceState = osso.DeviceState(self._osso)
221 self._deviceState.set_device_state_callback(self._on_device_state_change, 0)
223 _moduleLogger.warning("No device state support")
227 if alarm_handler.AlarmHandler is not alarm_handler._NoneAlarmHandler:
228 self._alarmHandler = alarm_handler.AlarmHandler()
230 self._alarmHandler = None
231 except (ImportError, OSError):
234 with gtk_toolbox.gtk_lock():
235 self._errorDisplay.push_exception()
237 if alarm_handler is None:
238 _moduleLogger.warning("No notification support")
239 if hildonize.IS_HILDON_SUPPORTED:
242 self._ledHandler = led_handler.LedHandler()
244 _moduleLogger.exception('LED Handling failed: "%s"' % str(e))
245 self._ledHandler = None
247 self._ledHandler = None
251 except (ImportError, OSError):
253 self._connection = None
254 if conic is not None:
255 self._connection = conic.Connection()
256 self._connection.connect("connection-event", self._on_connection_change, constants.__app_magic__)
257 self._connection.request_connection(conic.CONNECT_FLAG_NONE)
259 _moduleLogger.warning("No connection support")
261 with gtk_toolbox.gtk_lock():
262 self._errorDisplay.push_exception()
264 # Setup costly backends
266 from backends import gv_backend
267 from backends import file_backend
269 from backends import merge_backend
272 os.makedirs(constants._data_path_)
276 gvCookiePath = os.path.join(constants._data_path_, "gv_cookies.txt")
278 self._phoneBackends.update({
279 self.GV_BACKEND: gv_backend.GVDialer(gvCookiePath),
281 with gtk_toolbox.gtk_lock():
282 unifiedDialpad = gv_views.Dialpad(self._widgetTree, self._errorDisplay)
283 self._dialpads.update({
284 self.GV_BACKEND: unifiedDialpad,
286 self._accountViews.update({
287 self.GV_BACKEND: gv_views.AccountInfo(
288 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._alarmHandler, self._errorDisplay
291 self._accountViews[self.GV_BACKEND].save_everything = self._save_settings
292 self._recentViews.update({
293 self.GV_BACKEND: gv_views.RecentCallsView(
294 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
297 self._messagesViews.update({
298 self.GV_BACKEND: gv_views.MessagesView(
299 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
302 self._contactsViews.update({
303 self.GV_BACKEND: gv_views.ContactsView(
304 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
308 fsContactsPath = os.path.join(constants._data_path_, "contacts")
309 fileBackend = file_backend.FilesystemAddressBookFactory(fsContactsPath)
311 self._dialpads[self.GV_BACKEND].number_selected = self._select_action
312 self._recentViews[self.GV_BACKEND].number_selected = self._select_action
313 self._messagesViews[self.GV_BACKEND].number_selected = self._select_action
314 self._contactsViews[self.GV_BACKEND].number_selected = self._select_action
317 self._phoneBackends[self.GV_BACKEND],
320 mergedBook = merge_backend.MergedAddressBook(addressBooks, merge_backend.MergedAddressBook.advanced_lastname_sorter)
321 self._contactsViews[self.GV_BACKEND].append(mergedBook)
322 self._contactsViews[self.GV_BACKEND].extend(addressBooks)
323 self._contactsViews[self.GV_BACKEND].open_addressbook(*self._contactsViews[self.GV_BACKEND].get_addressbooks().next()[0][0:2])
326 "on_paste": self._on_paste,
327 "on_refresh": self._on_menu_refresh,
328 "on_clearcookies_clicked": self._on_clearcookies_clicked,
329 "on_about_activate": self._on_about_activate,
331 if hildonize.GTK_MENU_USED:
332 self._widgetTree.signal_autoconnect(callbackMapping)
333 self._notebook.connect("switch-page", self._on_notebook_switch_page)
334 self._widgetTree.get_widget("clearcookies").connect("clicked", self._on_clearcookies_clicked)
336 with gtk_toolbox.gtk_lock():
337 self._originalCurrentLabels = [
338 self._notebook.get_tab_label(self._notebook.get_nth_page(pageIndex)).get_text()
339 for pageIndex in xrange(self._notebook.get_n_pages())
341 self._notebookTapHandler = gtk_toolbox.TapOrHold(self._notebook)
342 self._notebookTapHandler.enable()
343 self._notebookTapHandler.on_tap = self._reset_tab_refresh
344 self._notebookTapHandler.on_hold = self._on_tab_refresh
345 self._notebookTapHandler.on_holding = self._set_tab_refresh
346 self._notebookTapHandler.on_cancel = self._reset_tab_refresh
348 config = ConfigParser.SafeConfigParser()
349 config.read(constants._user_settings_)
350 with gtk_toolbox.gtk_lock():
351 self.load_settings(config)
353 with gtk_toolbox.gtk_lock():
354 self._errorDisplay.push_exception()
356 self._initDone = True
357 self._spawn_attempt_login()
359 def _spawn_attempt_login(self, *args):
360 self._loginSink.send(args)
362 def _attempt_login(self, force = False):
364 @note This must be run outside of the UI lock
367 assert self._initDone, "Attempting login before app is fully loaded"
369 serviceId = self.NULL_BACKEND
371 if not force and self._defaultBackendId != self.NULL_BACKEND:
372 with gtk_toolbox.gtk_lock():
373 banner = hildonize.show_busy_banner_start(self._window, "Logging In...")
375 self.refresh_session()
376 serviceId = self._defaultBackendId
379 _moduleLogger.exception('Session refresh failed with the following message "%s"' % str(e))
381 with gtk_toolbox.gtk_lock():
382 hildonize.show_busy_banner_end(banner)
385 loggedIn, serviceId = self._login_by_user()
387 with gtk_toolbox.gtk_lock():
388 self._change_loggedin_status(serviceId)
390 hildonize.show_information_banner(self._window, "Logged In")
392 hildonize.show_information_banner(self._window, "Login Failed")
393 if not self._phoneBackends[self._defaultBackendId].get_callback_number():
394 # subtle reminder to the users to configure things
395 self._notebook.set_current_page(self.ACCOUNT_TAB)
398 with gtk_toolbox.gtk_lock():
399 self._errorDisplay.push_exception()
401 def refresh_session(self):
403 @note Thread agnostic
405 assert self._initDone, "Attempting login before app is fully loaded"
409 loggedIn = self._login_by_cookie()
411 loggedIn = self._login_by_settings()
414 raise RuntimeError("Login Failed")
416 def _login_by_cookie(self):
418 @note Thread agnostic
422 isQuickLoginPossible = self._phoneBackends[self._defaultBackendId].is_quick_login_possible()
423 if self._credentials != ("", "") and isQuickLoginPossible:
425 loggedIn = self._phoneBackends[self._defaultBackendId].is_authed()
428 _moduleLogger.info("Logged into %r through cookies" % self._phoneBackends[self._defaultBackendId])
430 # If the cookies are bad, scratch them completely
431 self._phoneBackends[self._defaultBackendId].logout()
435 def _login_by_settings(self):
437 @note Thread agnostic
439 if self._credentials == ("", ""):
440 # Don't bother with the settings if they are blank
443 username, password = self._credentials
444 loggedIn = self._phoneBackends[self._defaultBackendId].login(username, password)
446 self._credentials = username, password
447 _moduleLogger.info("Logged into %r through settings" % self._phoneBackends[self._defaultBackendId])
450 def _login_by_user(self):
452 @note This must be run outside of the UI lock
454 loggedIn, (username, password) = False, self._credentials
455 tmpServiceId = self.GV_BACKEND
457 with gtk_toolbox.gtk_lock():
458 credentials = self._credentialsDialog.request_credentials(
459 defaultCredentials = self._credentials
461 banner = hildonize.show_busy_banner_start(self._window, "Logging In...")
463 username, password = credentials
464 loggedIn = self._phoneBackends[tmpServiceId].login(username, password)
466 with gtk_toolbox.gtk_lock():
467 hildonize.show_busy_banner_end(banner)
470 serviceId = tmpServiceId
471 self._credentials = username, password
472 _moduleLogger.info("Logged into %r through user request" % self._phoneBackends[serviceId])
474 # Hint to the user that they are not logged in
475 serviceId = self.NULL_BACKEND
476 self._notebook.set_current_page(self.ACCOUNT_TAB)
478 return loggedIn, serviceId
480 def _select_action(self, action, number, message):
481 self.refresh_session()
483 self._on_dial_clicked(number)
484 elif action == "sms":
485 self._on_sms_clicked(number, message)
487 assert False, "Unknown action: %s" % action
489 def _change_loggedin_status(self, newStatus):
490 oldStatus = self._selectedBackendId
491 if oldStatus == newStatus:
494 self._dialpads[oldStatus].disable()
495 self._accountViews[oldStatus].disable()
496 self._recentViews[oldStatus].disable()
497 self._messagesViews[oldStatus].disable()
498 self._contactsViews[oldStatus].disable()
500 self._dialpads[newStatus].enable()
501 self._accountViews[newStatus].enable()
502 self._recentViews[newStatus].enable()
503 self._messagesViews[newStatus].enable()
504 self._contactsViews[newStatus].enable()
506 self._selectedBackendId = newStatus
508 self._accountViews[self._selectedBackendId].update()
509 self._refresh_active_tab()
511 def load_settings(self, config):
516 if not PROFILE_STARTUP:
517 self._defaultBackendId = config.getint(constants.__pretty_app_name__, "active")
519 self._defaultBackendId = self.NULL_BACKEND
521 config.get(constants.__pretty_app_name__, "bin_blob_%i" % i)
522 for i in xrange(len(self._credentials))
525 base64.b64decode(blob)
528 self._credentials = tuple(creds)
530 if self._alarmHandler is not None:
531 self._alarmHandler.load_settings(config, "alarm")
532 except ConfigParser.NoOptionError, e:
533 _moduleLogger.exception(
534 "Settings file %s is missing section %s" % (
535 constants._user_settings_,
539 except ConfigParser.NoSectionError, e:
540 _moduleLogger.exception(
541 "Settings file %s is missing section %s" % (
542 constants._user_settings_,
547 for backendId, view in itertools.chain(
548 self._dialpads.iteritems(),
549 self._accountViews.iteritems(),
550 self._messagesViews.iteritems(),
551 self._recentViews.iteritems(),
552 self._contactsViews.iteritems(),
554 sectionName = "%s - %s" % (backendId, view.name())
556 view.load_settings(config, sectionName)
557 except ConfigParser.NoOptionError, e:
558 _moduleLogger.exception(
559 "Settings file %s is missing section %s" % (
560 constants._user_settings_,
564 except ConfigParser.NoSectionError, e:
565 _moduleLogger.exception(
566 "Settings file %s is missing section %s" % (
567 constants._user_settings_,
573 previousOrientation = config.getint(constants.__pretty_app_name__, "orientation")
574 if previousOrientation == gtk.ORIENTATION_HORIZONTAL:
575 hildonize.window_to_landscape(self._window)
576 elif previousOrientation == gtk.ORIENTATION_VERTICAL:
577 hildonize.window_to_portrait(self._window)
578 except ConfigParser.NoOptionError, e:
579 _moduleLogger.exception(
580 "Settings file %s is missing section %s" % (
581 constants._user_settings_,
585 except ConfigParser.NoSectionError, e:
586 _moduleLogger.exception(
587 "Settings file %s is missing section %s" % (
588 constants._user_settings_,
593 def save_settings(self, config):
595 @note Thread Agnostic
597 # Because we now only support GVoice, if there are user credentials,
598 # always assume its using the GVoice backend
599 if self._credentials[0] and self._credentials[1]:
600 backend = self.GV_BACKEND
602 backend = self.NULL_BACKEND
604 config.add_section(constants.__pretty_app_name__)
605 config.set(constants.__pretty_app_name__, "active", str(backend))
606 config.set(constants.__pretty_app_name__, "orientation", str(int(gtk_toolbox.get_screen_orientation())))
607 for i, value in enumerate(self._credentials):
608 blob = base64.b64encode(value)
609 config.set(constants.__pretty_app_name__, "bin_blob_%i" % i, blob)
610 config.add_section("alarm")
611 if self._alarmHandler is not None:
612 self._alarmHandler.save_settings(config, "alarm")
614 for backendId, view in itertools.chain(
615 self._dialpads.iteritems(),
616 self._accountViews.iteritems(),
617 self._messagesViews.iteritems(),
618 self._recentViews.iteritems(),
619 self._contactsViews.iteritems(),
621 sectionName = "%s - %s" % (backendId, view.name())
622 config.add_section(sectionName)
623 view.save_settings(config, sectionName)
625 def _save_settings(self):
627 @note Thread Agnostic
629 config = ConfigParser.SafeConfigParser()
630 self.save_settings(config)
631 with open(constants._user_settings_, "wb") as configFile:
632 config.write(configFile)
634 def _refresh_active_tab(self):
635 pageIndex = self._notebook.get_current_page()
636 if pageIndex == self.CONTACTS_TAB:
637 self._contactsViews[self._selectedBackendId].update(force=True)
638 elif pageIndex == self.RECENT_TAB:
639 self._recentViews[self._selectedBackendId].update(force=True)
640 elif pageIndex == self.MESSAGES_TAB:
641 self._messagesViews[self._selectedBackendId].update(force=True)
643 if pageIndex in (self.RECENT_TAB, self.MESSAGES_TAB):
644 if self._ledHandler is not None:
645 self._ledHandler.off()
647 @gtk_toolbox.log_exception(_moduleLogger)
648 def _on_close(self, *args, **kwds):
651 self._save_settings()
654 self._deviceState.close()
655 except AttributeError:
656 pass # Either None or close was removed (in Fremantle)
659 except AttributeError:
660 pass # Either None or close was removed (in Fremantle)
664 def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData):
666 For shutdown or save_unsaved_data, our only state is cookies and I think the cookie manager handles that for us.
667 For system_inactivity, we have no background tasks to pause
669 @note Hildon specific
673 for backendId in self.BACKENDS:
674 self._phoneBackends[backendId].clear_caches()
675 self._contactsViews[self._selectedBackendId].clear_caches()
678 if save_unsaved_data or shutdown:
679 self._save_settings()
681 self._errorDisplay.push_exception()
683 def _on_connection_change(self, connection, event, magicIdentifier):
685 @note Hildon specific
690 status = event.get_status()
691 error = event.get_error()
692 iap_id = event.get_iap_id()
693 bearer = event.get_bearer_type()
695 if status == conic.STATUS_CONNECTED:
697 self._spawn_attempt_login()
698 elif status == conic.STATUS_DISCONNECTED:
700 self._defaultBackendId = self._selectedBackendId
701 self._change_loggedin_status(self.NULL_BACKEND)
703 self._errorDisplay.push_exception()
705 def _on_window_state_change(self, widget, event, *args):
707 @note Hildon specific
710 if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
711 self._isFullScreen = True
713 self._isFullScreen = False
715 self._errorDisplay.push_exception()
717 def _on_key_press(self, widget, event, *args):
719 @note Hildon specific
721 RETURN_TYPES = (gtk.keysyms.Return, gtk.keysyms.ISO_Enter, gtk.keysyms.KP_Enter)
724 event.keyval == gtk.keysyms.F6 or
725 event.keyval in RETURN_TYPES and event.get_state() & gtk.gdk.CONTROL_MASK
727 if self._isFullScreen:
728 self._window.unfullscreen()
730 self._window.fullscreen()
731 elif event.keyval == ord("l") and event.get_state() & gtk.gdk.CONTROL_MASK:
732 with open(constants._user_logpath_, "r") as f:
733 logLines = f.xreadlines()
734 log = "".join(logLines)
735 self._clipboard.set_text(str(log))
736 elif event.keyval == ord("r") and event.get_state() & gtk.gdk.CONTROL_MASK:
737 self._refresh_active_tab()
739 self._errorDisplay.push_exception()
741 def _on_clearcookies_clicked(self, *args):
743 self._phoneBackends[self._selectedBackendId].logout()
744 self._accountViews[self._selectedBackendId].clear()
745 self._recentViews[self._selectedBackendId].clear()
746 self._messagesViews[self._selectedBackendId].clear()
747 self._contactsViews[self._selectedBackendId].clear()
748 self._change_loggedin_status(self.NULL_BACKEND)
750 self._spawn_attempt_login(True)
752 self._errorDisplay.push_exception()
754 def _on_notebook_switch_page(self, notebook, page, pageIndex):
756 self._reset_tab_refresh()
758 didRecentUpdate = False
759 didMessagesUpdate = False
761 if pageIndex == self.RECENT_TAB:
762 didRecentUpdate = self._recentViews[self._selectedBackendId].update()
763 elif pageIndex == self.MESSAGES_TAB:
764 didMessagesUpdate = self._messagesViews[self._selectedBackendId].update()
765 elif pageIndex == self.CONTACTS_TAB:
766 self._contactsViews[self._selectedBackendId].update()
767 elif pageIndex == self.ACCOUNT_TAB:
768 self._accountViews[self._selectedBackendId].update()
770 if didRecentUpdate or didMessagesUpdate:
771 if self._ledHandler is not None:
772 self._ledHandler.off()
774 self._errorDisplay.push_exception()
776 def _set_tab_refresh(self, *args):
778 pageIndex = self._notebook.get_current_page()
779 child = self._notebook.get_nth_page(pageIndex)
780 self._notebook.get_tab_label(child).set_text("Refresh?")
782 self._errorDisplay.push_exception()
785 def _reset_tab_refresh(self, *args):
787 pageIndex = self._notebook.get_current_page()
788 child = self._notebook.get_nth_page(pageIndex)
789 self._notebook.get_tab_label(child).set_text(self._originalCurrentLabels[pageIndex])
791 self._errorDisplay.push_exception()
794 def _on_tab_refresh(self, *args):
796 self._refresh_active_tab()
797 self._reset_tab_refresh()
799 self._errorDisplay.push_exception()
802 def _on_sms_clicked(self, number, message):
804 assert number, "No number specified"
805 assert message, "Empty message"
807 loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
810 self._errorDisplay.push_exception()
814 self._errorDisplay.push_message(
815 "Backend link with GoogleVoice is not working, please try again"
821 self._phoneBackends[self._selectedBackendId].send_sms(number, message)
822 hildonize.show_information_banner(self._window, "Sending to %s" % number)
823 _moduleLogger.info("Sending SMS to %s" % number)
826 self._errorDisplay.push_exception()
829 self._dialpads[self._selectedBackendId].clear()
831 self._errorDisplay.push_exception()
833 def _on_dial_clicked(self, number):
835 assert number, "No number to call"
837 loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
840 self._errorDisplay.push_exception()
844 self._errorDisplay.push_message(
845 "Backend link with GoogleVoice is not working, please try again"
851 assert self._phoneBackends[self._selectedBackendId].get_callback_number() != "", "No callback number specified"
852 self._phoneBackends[self._selectedBackendId].call(number)
853 hildonize.show_information_banner(self._window, "Calling %s" % number)
854 _moduleLogger.info("Calling %s" % number)
857 self._errorDisplay.push_exception()
860 self._dialpads[self._selectedBackendId].clear()
862 self._errorDisplay.push_exception()
864 def _on_menu_refresh(self, *args):
866 self._refresh_active_tab()
868 self._errorDisplay.push_exception()
870 def _on_paste(self, *args):
872 contents = self._clipboard.wait_for_text()
873 if contents is not None:
874 self._dialpads[self._selectedBackendId].set_number(contents)
876 self._errorDisplay.push_exception()
878 def _on_about_activate(self, *args):
880 dlg = gtk.AboutDialog()
881 dlg.set_name(constants.__pretty_app_name__)
882 dlg.set_version("%s-%d" % (constants.__version__, constants.__build__))
883 dlg.set_copyright("Copyright 2008 - LGPL")
884 dlg.set_comments("Dialcentral is a touch screen enhanced interface to your GoogleVoice account. This application is not affiliated with Google in any way")
885 dlg.set_website("http://gc-dialer.garage.maemo.org/")
886 dlg.set_authors(["<z2n@merctech.com>", "Eric Warnke <ericew@gmail.com>", "Ed Page <eopage@byu.net>"])
890 self._errorDisplay.push_exception()
896 failureCount, testCount = doctest.testmod()
898 print "Tests Successful"
905 _lock_file = os.path.join(constants._data_path_, ".lock")
906 #with gtk_toolbox.flock(_lock_file, 0):
907 gtk.gdk.threads_init()
909 if hildonize.IS_HILDON_SUPPORTED:
910 gtk.set_application_name(constants.__pretty_app_name__)
911 handle = Dialcentral()
912 if not PROFILE_STARTUP:
916 class DummyOptions(object):
922 if __name__ == "__main__":
923 logging.basicConfig(level=logging.DEBUG)
925 if len(sys.argv) > 1:
931 if optparse is not None:
932 parser = optparse.OptionParser()
933 parser.add_option("-t", "--test", action="store_true", dest="test", help="Run tests")
934 (commandOptions, commandArgs) = parser.parse_args()
936 commandOptions = DummyOptions()
939 if commandOptions.test: