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._clipboard = gtk.clipboard_get()
87 self._credentials = ("", "")
88 self._selectedBackendId = self.NULL_BACKEND
89 self._defaultBackendId = self.GV_BACKEND
90 self._phoneBackends = None
92 self._accountViews = None
93 self._messagesViews = None
94 self._recentViews = None
95 self._contactsViews = None
96 self._alarmHandler = None
97 self._ledHandler = None
98 self._originalCurrentLabels = []
100 for path in self._glade_files:
101 if os.path.isfile(path):
102 self._widgetTree = gtk.glade.XML(path)
105 display_error_message("Cannot find dialcentral.glade")
109 self._window = self._widgetTree.get_widget("mainWindow")
110 self._notebook = self._widgetTree.get_widget("notebook")
111 self._errorDisplay = gtk_toolbox.ErrorDisplay(self._widgetTree)
112 self._credentialsDialog = gtk_toolbox.LoginWindow(self._widgetTree)
114 self._isFullScreen = False
115 self._app = hildonize.get_app_class()()
116 self._window = hildonize.hildonize_window(self._app, self._window)
117 hildonize.hildonize_text_entry(self._widgetTree.get_widget("usernameentry"))
118 hildonize.hildonize_password_entry(self._widgetTree.get_widget("passwordentry"))
120 for scrollingWidgetName in (
121 'recent_scrolledwindow',
122 'message_scrolledwindow',
123 'contacts_scrolledwindow',
124 "smsMessages_scrolledwindow",
126 scrollingWidget = self._widgetTree.get_widget(scrollingWidgetName)
127 assert scrollingWidget is not None, scrollingWidgetName
128 hildonize.hildonize_scrollwindow(scrollingWidget)
129 for scrollingWidgetName in (
130 "smsMessage_scrolledEntry",
132 scrollingWidget = self._widgetTree.get_widget(scrollingWidgetName)
133 assert scrollingWidget is not None, scrollingWidgetName
134 hildonize.hildonize_scrollwindow_with_viewport(scrollingWidget)
138 "addressbookSelectButton",
142 "callbackSelectButton",
143 "minutesEntryButton",
145 "phoneTypeSelection",
147 button = self._widgetTree.get_widget(buttonName)
148 assert button is not None, buttonName
149 hildonize.set_button_thumb_selectable(button)
151 replacementButtons = [gtk.Button("Test")]
152 menu = hildonize.hildonize_menu(
154 self._widgetTree.get_widget("dialpad_menubar"),
158 self._window.connect("key-press-event", self._on_key_press)
159 self._window.connect("window-state-event", self._on_window_state_change)
160 if not hildonize.IS_HILDON_SUPPORTED:
161 _moduleLogger.warning("No hildonization support")
163 hildonize.set_application_title(self._window, "%s" % constants.__pretty_app_name__)
165 self._window.connect("destroy", self._on_close)
166 self._window.set_default_size(800, 300)
167 self._window.show_all()
169 self._loginSink = gtk_toolbox.threaded_stage(
172 gtk_toolbox.null_sink(),
176 if not PROFILE_STARTUP:
177 backgroundSetup = threading.Thread(target=self._idle_setup)
178 backgroundSetup.setDaemon(True)
179 backgroundSetup.start()
183 def _idle_setup(self):
185 If something can be done after the UI loads, push it here so it's not blocking the UI
187 # Barebones UI handlers
189 from backends import null_backend
192 self._phoneBackends = {self.NULL_BACKEND: null_backend.NullDialer()}
193 with gtk_toolbox.gtk_lock():
194 self._dialpads = {self.NULL_BACKEND: null_views.Dialpad(self._widgetTree)}
195 self._accountViews = {self.NULL_BACKEND: null_views.AccountInfo(self._widgetTree)}
196 self._recentViews = {self.NULL_BACKEND: null_views.RecentCallsView(self._widgetTree)}
197 self._messagesViews = {self.NULL_BACKEND: null_views.MessagesView(self._widgetTree)}
198 self._contactsViews = {self.NULL_BACKEND: null_views.ContactsView(self._widgetTree)}
200 self._dialpads[self._selectedBackendId].enable()
201 self._accountViews[self._selectedBackendId].enable()
202 self._recentViews[self._selectedBackendId].enable()
203 self._messagesViews[self._selectedBackendId].enable()
204 self._contactsViews[self._selectedBackendId].enable()
206 with gtk_toolbox.gtk_lock():
207 self._errorDisplay.push_exception()
209 # Setup maemo specifics
213 except (ImportError, OSError):
217 self._osso = osso.Context(constants.__app_name__, constants.__version__, False)
218 device = osso.DeviceState(self._osso)
219 device.set_device_state_callback(self._on_device_state_change, 0)
221 _moduleLogger.warning("No device state support")
225 if alarm_handler.AlarmHandler is not alarm_handler._NoneAlarmHandler:
226 self._alarmHandler = alarm_handler.AlarmHandler()
228 self._alarmHandler = None
229 except (ImportError, OSError):
232 with gtk_toolbox.gtk_lock():
233 self._errorDisplay.push_exception()
235 if alarm_handler is None:
236 _moduleLogger.warning("No notification support")
237 if hildonize.IS_HILDON_SUPPORTED:
240 self._ledHandler = led_handler.LedHandler()
242 _moduleLogger.exception('LED Handling failed: "%s"' % str(e))
243 self._ledHandler = None
245 self._ledHandler = None
249 except (ImportError, OSError):
251 self._connection = None
252 if conic is not None:
253 self._connection = conic.Connection()
254 self._connection.connect("connection-event", self._on_connection_change, constants.__app_magic__)
255 self._connection.request_connection(conic.CONNECT_FLAG_NONE)
257 _moduleLogger.warning("No connection support")
259 with gtk_toolbox.gtk_lock():
260 self._errorDisplay.push_exception()
262 # Setup costly backends
264 from backends import gv_backend
265 from backends import file_backend
267 from backends import merge_backend
270 os.makedirs(constants._data_path_)
274 gvCookiePath = os.path.join(constants._data_path_, "gv_cookies.txt")
276 self._phoneBackends.update({
277 self.GV_BACKEND: gv_backend.GVDialer(gvCookiePath),
279 with gtk_toolbox.gtk_lock():
280 unifiedDialpad = gv_views.Dialpad(self._widgetTree, self._errorDisplay)
281 self._dialpads.update({
282 self.GV_BACKEND: unifiedDialpad,
284 self._accountViews.update({
285 self.GV_BACKEND: gv_views.AccountInfo(
286 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._alarmHandler, self._errorDisplay
289 self._accountViews[self.GV_BACKEND].save_everything = self._save_settings
290 self._recentViews.update({
291 self.GV_BACKEND: gv_views.RecentCallsView(
292 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
295 self._messagesViews.update({
296 self.GV_BACKEND: gv_views.MessagesView(
297 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
300 self._contactsViews.update({
301 self.GV_BACKEND: gv_views.ContactsView(
302 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
306 fsContactsPath = os.path.join(constants._data_path_, "contacts")
307 fileBackend = file_backend.FilesystemAddressBookFactory(fsContactsPath)
309 self._dialpads[self.GV_BACKEND].number_selected = self._select_action
310 self._recentViews[self.GV_BACKEND].number_selected = self._select_action
311 self._messagesViews[self.GV_BACKEND].number_selected = self._select_action
312 self._contactsViews[self.GV_BACKEND].number_selected = self._select_action
315 self._phoneBackends[self.GV_BACKEND],
318 mergedBook = merge_backend.MergedAddressBook(addressBooks, merge_backend.MergedAddressBook.advanced_lastname_sorter)
319 self._contactsViews[self.GV_BACKEND].append(mergedBook)
320 self._contactsViews[self.GV_BACKEND].extend(addressBooks)
321 self._contactsViews[self.GV_BACKEND].open_addressbook(*self._contactsViews[self.GV_BACKEND].get_addressbooks().next()[0][0:2])
324 "on_paste": self._on_paste,
325 "on_refresh": self._on_menu_refresh,
326 "on_clearcookies_clicked": self._on_clearcookies_clicked,
327 "on_about_activate": self._on_about_activate,
329 if hildonize.GTK_MENU_USED:
330 self._widgetTree.signal_autoconnect(callbackMapping)
331 self._notebook.connect("switch-page", self._on_notebook_switch_page)
332 self._widgetTree.get_widget("clearcookies").connect("clicked", self._on_clearcookies_clicked)
334 with gtk_toolbox.gtk_lock():
335 self._originalCurrentLabels = [
336 self._notebook.get_tab_label(self._notebook.get_nth_page(pageIndex)).get_text()
337 for pageIndex in xrange(self._notebook.get_n_pages())
339 self._notebookTapHandler = gtk_toolbox.TapOrHold(self._notebook)
340 self._notebookTapHandler.enable()
341 self._notebookTapHandler.on_tap = self._reset_tab_refresh
342 self._notebookTapHandler.on_hold = self._on_tab_refresh
343 self._notebookTapHandler.on_holding = self._set_tab_refresh
344 self._notebookTapHandler.on_cancel = self._reset_tab_refresh
346 config = ConfigParser.SafeConfigParser()
347 config.read(constants._user_settings_)
348 with gtk_toolbox.gtk_lock():
349 self.load_settings(config)
351 with gtk_toolbox.gtk_lock():
352 self._errorDisplay.push_exception()
354 self._initDone = True
355 self._spawn_attempt_login()
357 def _spawn_attempt_login(self, *args):
358 self._loginSink.send(args)
360 def _attempt_login(self, force = False):
362 @note This must be run outside of the UI lock
365 assert self._initDone, "Attempting login before app is fully loaded"
367 serviceId = self.NULL_BACKEND
369 if not force and self._defaultBackendId != self.NULL_BACKEND:
370 with gtk_toolbox.gtk_lock():
371 banner = hildonize.show_busy_banner_start(self._window, "Logging In...")
373 self.refresh_session()
374 serviceId = self._defaultBackendId
377 _moduleLogger.exception('Session refresh failed with the following message "%s"' % str(e))
379 with gtk_toolbox.gtk_lock():
380 hildonize.show_busy_banner_end(banner)
383 loggedIn, serviceId = self._login_by_user()
385 with gtk_toolbox.gtk_lock():
386 self._change_loggedin_status(serviceId)
388 hildonize.show_information_banner(self._window, "Logged In")
390 hildonize.show_information_banner(self._window, "Login Failed")
391 if not self._phoneBackends[self._defaultBackendId].get_callback_number():
392 # subtle reminder to the users to configure things
393 self._notebook.set_current_page(self.ACCOUNT_TAB)
396 with gtk_toolbox.gtk_lock():
397 self._errorDisplay.push_exception()
399 def refresh_session(self):
401 @note Thread agnostic
403 assert self._initDone, "Attempting login before app is fully loaded"
407 loggedIn = self._login_by_cookie()
409 loggedIn = self._login_by_settings()
412 raise RuntimeError("Login Failed")
414 def _login_by_cookie(self):
416 @note Thread agnostic
419 if self._credentials == ("", ""):
420 # Disallow logging in by cookie alone, without credentials
424 loggedIn = self._phoneBackends[self._defaultBackendId].is_authed()
427 _moduleLogger.info("Logged into %r through cookies" % self._phoneBackends[self._defaultBackendId])
429 # If the cookies are bad, scratch them completely
430 self._phoneBackends[self._defaultBackendId].logout()
434 def _login_by_settings(self):
436 @note Thread agnostic
438 if self._credentials == ("", ""):
439 # Don't bother with the settings if they are blank
442 username, password = self._credentials
443 loggedIn = self._phoneBackends[self._defaultBackendId].login(username, password)
445 self._credentials = username, password
446 _moduleLogger.info("Logged into %r through settings" % self._phoneBackends[self._defaultBackendId])
449 def _login_by_user(self):
451 @note This must be run outside of the UI lock
453 loggedIn, (username, password) = False, self._credentials
454 tmpServiceId = self.GV_BACKEND
456 with gtk_toolbox.gtk_lock():
457 credentials = self._credentialsDialog.request_credentials(
458 defaultCredentials = self._credentials
460 banner = hildonize.show_busy_banner_start(self._window, "Logging In...")
462 username, password = credentials
463 loggedIn = self._phoneBackends[tmpServiceId].login(username, password)
465 with gtk_toolbox.gtk_lock():
466 hildonize.show_busy_banner_end(banner)
469 serviceId = tmpServiceId
470 self._credentials = username, password
471 _moduleLogger.info("Logged into %r through user request" % self._phoneBackends[serviceId])
473 # Hint to the user that they are not logged in
474 serviceId = self.NULL_BACKEND
475 self._notebook.set_current_page(self.ACCOUNT_TAB)
477 return loggedIn, serviceId
479 def _select_action(self, action, number, message):
480 self.refresh_session()
482 self._on_dial_clicked(number)
483 elif action == "sms":
484 self._on_sms_clicked(number, message)
486 assert False, "Unknown action: %s" % action
488 def _change_loggedin_status(self, newStatus):
489 oldStatus = self._selectedBackendId
490 if oldStatus == newStatus:
493 self._dialpads[oldStatus].disable()
494 self._accountViews[oldStatus].disable()
495 self._recentViews[oldStatus].disable()
496 self._messagesViews[oldStatus].disable()
497 self._contactsViews[oldStatus].disable()
499 self._dialpads[newStatus].enable()
500 self._accountViews[newStatus].enable()
501 self._recentViews[newStatus].enable()
502 self._messagesViews[newStatus].enable()
503 self._contactsViews[newStatus].enable()
505 self._selectedBackendId = newStatus
507 self._accountViews[self._selectedBackendId].update()
508 self._refresh_active_tab()
510 def load_settings(self, config):
515 if not PROFILE_STARTUP:
516 self._defaultBackendId = config.getint(constants.__pretty_app_name__, "active")
518 self._defaultBackendId = self.NULL_BACKEND
520 config.get(constants.__pretty_app_name__, "bin_blob_%i" % i)
521 for i in xrange(len(self._credentials))
524 base64.b64decode(blob)
527 self._credentials = tuple(creds)
529 if self._alarmHandler is not None:
530 self._alarmHandler.load_settings(config, "alarm")
531 except ConfigParser.NoOptionError, e:
532 _moduleLogger.exception(
533 "Settings file %s is missing section %s" % (
534 constants._user_settings_,
538 except ConfigParser.NoSectionError, e:
539 _moduleLogger.exception(
540 "Settings file %s is missing section %s" % (
541 constants._user_settings_,
546 for backendId, view in itertools.chain(
547 self._dialpads.iteritems(),
548 self._accountViews.iteritems(),
549 self._messagesViews.iteritems(),
550 self._recentViews.iteritems(),
551 self._contactsViews.iteritems(),
553 sectionName = "%s - %s" % (backendId, view.name())
555 view.load_settings(config, sectionName)
556 except ConfigParser.NoOptionError, e:
557 _moduleLogger.exception(
558 "Settings file %s is missing section %s" % (
559 constants._user_settings_,
563 except ConfigParser.NoSectionError, e:
564 _moduleLogger.exception(
565 "Settings file %s is missing section %s" % (
566 constants._user_settings_,
572 previousOrientation = config.getint(constants.__pretty_app_name__, "orientation")
573 if previousOrientation == gtk.ORIENTATION_HORIZONTAL:
574 hildonize.window_to_landscape(self._window)
575 elif previousOrientation == gtk.ORIENTATION_VERTICAL:
576 hildonize.window_to_portrait(self._window)
577 except ConfigParser.NoOptionError, e:
578 _moduleLogger.exception(
579 "Settings file %s is missing section %s" % (
580 constants._user_settings_,
584 except ConfigParser.NoSectionError, e:
585 _moduleLogger.exception(
586 "Settings file %s is missing section %s" % (
587 constants._user_settings_,
592 def save_settings(self, config):
594 @note Thread Agnostic
596 # Because we now only support GVoice, if there are user credentials,
597 # always assume its using the GVoice backend
598 if self._credentials[0] and self._credentials[1]:
599 backend = self.GV_BACKEND
601 backend = self.NULL_BACKEND
603 config.add_section(constants.__pretty_app_name__)
604 config.set(constants.__pretty_app_name__, "active", str(backend))
605 config.set(constants.__pretty_app_name__, "orientation", str(int(gtk_toolbox.get_screen_orientation())))
606 for i, value in enumerate(self._credentials):
607 blob = base64.b64encode(value)
608 config.set(constants.__pretty_app_name__, "bin_blob_%i" % i, blob)
609 config.add_section("alarm")
610 if self._alarmHandler is not None:
611 self._alarmHandler.save_settings(config, "alarm")
613 for backendId, view in itertools.chain(
614 self._dialpads.iteritems(),
615 self._accountViews.iteritems(),
616 self._messagesViews.iteritems(),
617 self._recentViews.iteritems(),
618 self._contactsViews.iteritems(),
620 sectionName = "%s - %s" % (backendId, view.name())
621 config.add_section(sectionName)
622 view.save_settings(config, sectionName)
624 def _save_settings(self):
626 @note Thread Agnostic
628 config = ConfigParser.SafeConfigParser()
629 self.save_settings(config)
630 with open(constants._user_settings_, "wb") as configFile:
631 config.write(configFile)
633 def _refresh_active_tab(self):
634 pageIndex = self._notebook.get_current_page()
635 if pageIndex == self.CONTACTS_TAB:
636 self._contactsViews[self._selectedBackendId].update(force=True)
637 elif pageIndex == self.RECENT_TAB:
638 self._recentViews[self._selectedBackendId].update(force=True)
639 elif pageIndex == self.MESSAGES_TAB:
640 self._messagesViews[self._selectedBackendId].update(force=True)
642 if pageIndex in (self.RECENT_TAB, self.MESSAGES_TAB):
643 if self._ledHandler is not None:
644 self._ledHandler.off()
646 def _on_close(self, *args, **kwds):
648 if self._osso is not None:
652 self._save_settings()
656 def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData):
658 For shutdown or save_unsaved_data, our only state is cookies and I think the cookie manager handles that for us.
659 For system_inactivity, we have no background tasks to pause
661 @note Hildon specific
665 for backendId in self.BACKENDS:
666 self._phoneBackends[backendId].clear_caches()
667 self._contactsViews[self._selectedBackendId].clear_caches()
670 if save_unsaved_data or shutdown:
671 self._save_settings()
673 self._errorDisplay.push_exception()
675 def _on_connection_change(self, connection, event, magicIdentifier):
677 @note Hildon specific
682 status = event.get_status()
683 error = event.get_error()
684 iap_id = event.get_iap_id()
685 bearer = event.get_bearer_type()
687 if status == conic.STATUS_CONNECTED:
689 self._spawn_attempt_login()
690 elif status == conic.STATUS_DISCONNECTED:
692 self._defaultBackendId = self._selectedBackendId
693 self._change_loggedin_status(self.NULL_BACKEND)
695 self._errorDisplay.push_exception()
697 def _on_window_state_change(self, widget, event, *args):
699 @note Hildon specific
702 if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
703 self._isFullScreen = True
705 self._isFullScreen = False
707 self._errorDisplay.push_exception()
709 def _on_key_press(self, widget, event, *args):
711 @note Hildon specific
713 RETURN_TYPES = (gtk.keysyms.Return, gtk.keysyms.ISO_Enter, gtk.keysyms.KP_Enter)
716 event.keyval == gtk.keysyms.F6 or
717 event.keyval in RETURN_TYPES and event.get_state() & gtk.gdk.CONTROL_MASK
719 if self._isFullScreen:
720 self._window.unfullscreen()
722 self._window.fullscreen()
723 elif event.keyval == ord("l") and event.get_state() & gtk.gdk.CONTROL_MASK:
724 with open(constants._user_logpath_, "r") as f:
725 logLines = f.xreadlines()
726 log = "".join(logLines)
727 self._clipboard.set_text(str(log))
729 self._errorDisplay.push_exception()
731 def _on_clearcookies_clicked(self, *args):
733 self._phoneBackends[self._selectedBackendId].logout()
734 self._accountViews[self._selectedBackendId].clear()
735 self._recentViews[self._selectedBackendId].clear()
736 self._messagesViews[self._selectedBackendId].clear()
737 self._contactsViews[self._selectedBackendId].clear()
738 self._change_loggedin_status(self.NULL_BACKEND)
740 self._spawn_attempt_login(True)
742 self._errorDisplay.push_exception()
744 def _on_notebook_switch_page(self, notebook, page, pageIndex):
746 self._reset_tab_refresh()
748 didRecentUpdate = False
749 didMessagesUpdate = False
751 if pageIndex == self.RECENT_TAB:
752 didRecentUpdate = self._recentViews[self._selectedBackendId].update()
753 elif pageIndex == self.MESSAGES_TAB:
754 didMessagesUpdate = self._messagesViews[self._selectedBackendId].update()
755 elif pageIndex == self.CONTACTS_TAB:
756 self._contactsViews[self._selectedBackendId].update()
757 elif pageIndex == self.ACCOUNT_TAB:
758 self._accountViews[self._selectedBackendId].update()
760 if didRecentUpdate or didMessagesUpdate:
761 if self._ledHandler is not None:
762 self._ledHandler.off()
764 self._errorDisplay.push_exception()
766 def _set_tab_refresh(self, *args):
768 pageIndex = self._notebook.get_current_page()
769 child = self._notebook.get_nth_page(pageIndex)
770 self._notebook.get_tab_label(child).set_text("Refresh?")
772 self._errorDisplay.push_exception()
775 def _reset_tab_refresh(self, *args):
777 pageIndex = self._notebook.get_current_page()
778 child = self._notebook.get_nth_page(pageIndex)
779 self._notebook.get_tab_label(child).set_text(self._originalCurrentLabels[pageIndex])
781 self._errorDisplay.push_exception()
784 def _on_tab_refresh(self, *args):
786 self._refresh_active_tab()
787 self._reset_tab_refresh()
789 self._errorDisplay.push_exception()
792 def _on_sms_clicked(self, number, message):
794 assert number, "No number specified"
795 assert message, "Empty message"
797 loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
800 self._errorDisplay.push_exception()
804 self._errorDisplay.push_message(
805 "Backend link with GoogleVoice is not working, please try again"
811 self._phoneBackends[self._selectedBackendId].send_sms(number, message)
812 hildonize.show_information_banner(self._window, "Sending to %s" % number)
815 self._errorDisplay.push_exception()
818 self._dialpads[self._selectedBackendId].clear()
820 self._errorDisplay.push_exception()
822 def _on_dial_clicked(self, number):
824 assert number, "No number to call"
826 loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
829 self._errorDisplay.push_exception()
833 self._errorDisplay.push_message(
834 "Backend link with GoogleVoice is not working, please try again"
840 assert self._phoneBackends[self._selectedBackendId].get_callback_number() != "", "No callback number specified"
841 self._phoneBackends[self._selectedBackendId].dial(number)
842 hildonize.show_information_banner(self._window, "Calling %s" % number)
845 self._errorDisplay.push_exception()
848 self._dialpads[self._selectedBackendId].clear()
850 self._errorDisplay.push_exception()
852 def _on_menu_refresh(self, *args):
854 self._refresh_active_tab()
856 self._errorDisplay.push_exception()
858 def _on_paste(self, *args):
860 contents = self._clipboard.wait_for_text()
861 if contents is not None:
862 self._dialpads[self._selectedBackendId].set_number(contents)
864 self._errorDisplay.push_exception()
866 def _on_about_activate(self, *args):
868 dlg = gtk.AboutDialog()
869 dlg.set_name(constants.__pretty_app_name__)
870 dlg.set_version("%s-%d" % (constants.__version__, constants.__build__))
871 dlg.set_copyright("Copyright 2008 - LGPL")
872 dlg.set_comments("Dialcentral is a touch screen enhanced interface to your GoogleVoice account. This application is not affiliated with Google in any way")
873 dlg.set_website("http://gc-dialer.garage.maemo.org/")
874 dlg.set_authors(["<z2n@merctech.com>", "Eric Warnke <ericew@gmail.com>", "Ed Page <edpage@byu.net>"])
878 self._errorDisplay.push_exception()
884 failureCount, testCount = doctest.testmod()
886 print "Tests Successful"
893 _lock_file = os.path.join(constants._data_path_, ".lock")
894 #with gtk_toolbox.flock(_lock_file, 0):
895 gtk.gdk.threads_init()
897 if hildonize.IS_HILDON_SUPPORTED:
898 gtk.set_application_name(constants.__pretty_app_name__)
899 handle = Dialcentral()
900 if not PROFILE_STARTUP:
904 class DummyOptions(object):
910 if __name__ == "__main__":
911 logging.basicConfig(level=logging.DEBUG)
913 if len(sys.argv) > 1:
919 if optparse is not None:
920 parser = optparse.OptionParser()
921 parser.add_option("-t", "--test", action="store_true", dest="test", help="Run tests")
922 (commandOptions, commandArgs) = parser.parse_args()
924 commandOptions = DummyOptions()
927 if commandOptions.test: