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
420 isQuickLoginPossible = self._phoneBackends[self._defaultBackendId].is_quick_login_possible()
421 if self._credentials != ("", "") and isQuickLoginPossible:
423 loggedIn = self._phoneBackends[self._defaultBackendId].is_authed()
426 _moduleLogger.info("Logged into %r through cookies" % self._phoneBackends[self._defaultBackendId])
428 # If the cookies are bad, scratch them completely
429 self._phoneBackends[self._defaultBackendId].logout()
433 def _login_by_settings(self):
435 @note Thread agnostic
437 if self._credentials == ("", ""):
438 # Don't bother with the settings if they are blank
441 username, password = self._credentials
442 loggedIn = self._phoneBackends[self._defaultBackendId].login(username, password)
444 self._credentials = username, password
445 _moduleLogger.info("Logged into %r through settings" % self._phoneBackends[self._defaultBackendId])
448 def _login_by_user(self):
450 @note This must be run outside of the UI lock
452 loggedIn, (username, password) = False, self._credentials
453 tmpServiceId = self.GV_BACKEND
455 with gtk_toolbox.gtk_lock():
456 credentials = self._credentialsDialog.request_credentials(
457 defaultCredentials = self._credentials
459 banner = hildonize.show_busy_banner_start(self._window, "Logging In...")
461 username, password = credentials
462 loggedIn = self._phoneBackends[tmpServiceId].login(username, password)
464 with gtk_toolbox.gtk_lock():
465 hildonize.show_busy_banner_end(banner)
468 serviceId = tmpServiceId
469 self._credentials = username, password
470 _moduleLogger.info("Logged into %r through user request" % self._phoneBackends[serviceId])
472 # Hint to the user that they are not logged in
473 serviceId = self.NULL_BACKEND
474 self._notebook.set_current_page(self.ACCOUNT_TAB)
476 return loggedIn, serviceId
478 def _select_action(self, action, number, message):
479 self.refresh_session()
481 self._on_dial_clicked(number)
482 elif action == "sms":
483 self._on_sms_clicked(number, message)
485 assert False, "Unknown action: %s" % action
487 def _change_loggedin_status(self, newStatus):
488 oldStatus = self._selectedBackendId
489 if oldStatus == newStatus:
492 self._dialpads[oldStatus].disable()
493 self._accountViews[oldStatus].disable()
494 self._recentViews[oldStatus].disable()
495 self._messagesViews[oldStatus].disable()
496 self._contactsViews[oldStatus].disable()
498 self._dialpads[newStatus].enable()
499 self._accountViews[newStatus].enable()
500 self._recentViews[newStatus].enable()
501 self._messagesViews[newStatus].enable()
502 self._contactsViews[newStatus].enable()
504 self._selectedBackendId = newStatus
506 self._accountViews[self._selectedBackendId].update()
507 self._refresh_active_tab()
509 def load_settings(self, config):
514 if not PROFILE_STARTUP:
515 self._defaultBackendId = config.getint(constants.__pretty_app_name__, "active")
517 self._defaultBackendId = self.NULL_BACKEND
519 config.get(constants.__pretty_app_name__, "bin_blob_%i" % i)
520 for i in xrange(len(self._credentials))
523 base64.b64decode(blob)
526 self._credentials = tuple(creds)
528 if self._alarmHandler is not None:
529 self._alarmHandler.load_settings(config, "alarm")
530 except ConfigParser.NoOptionError, e:
531 _moduleLogger.exception(
532 "Settings file %s is missing section %s" % (
533 constants._user_settings_,
537 except ConfigParser.NoSectionError, e:
538 _moduleLogger.exception(
539 "Settings file %s is missing section %s" % (
540 constants._user_settings_,
545 for backendId, view in itertools.chain(
546 self._dialpads.iteritems(),
547 self._accountViews.iteritems(),
548 self._messagesViews.iteritems(),
549 self._recentViews.iteritems(),
550 self._contactsViews.iteritems(),
552 sectionName = "%s - %s" % (backendId, view.name())
554 view.load_settings(config, sectionName)
555 except ConfigParser.NoOptionError, e:
556 _moduleLogger.exception(
557 "Settings file %s is missing section %s" % (
558 constants._user_settings_,
562 except ConfigParser.NoSectionError, e:
563 _moduleLogger.exception(
564 "Settings file %s is missing section %s" % (
565 constants._user_settings_,
571 previousOrientation = config.getint(constants.__pretty_app_name__, "orientation")
572 if previousOrientation == gtk.ORIENTATION_HORIZONTAL:
573 hildonize.window_to_landscape(self._window)
574 elif previousOrientation == gtk.ORIENTATION_VERTICAL:
575 hildonize.window_to_portrait(self._window)
576 except ConfigParser.NoOptionError, e:
577 _moduleLogger.exception(
578 "Settings file %s is missing section %s" % (
579 constants._user_settings_,
583 except ConfigParser.NoSectionError, e:
584 _moduleLogger.exception(
585 "Settings file %s is missing section %s" % (
586 constants._user_settings_,
591 def save_settings(self, config):
593 @note Thread Agnostic
595 # Because we now only support GVoice, if there are user credentials,
596 # always assume its using the GVoice backend
597 if self._credentials[0] and self._credentials[1]:
598 backend = self.GV_BACKEND
600 backend = self.NULL_BACKEND
602 config.add_section(constants.__pretty_app_name__)
603 config.set(constants.__pretty_app_name__, "active", str(backend))
604 config.set(constants.__pretty_app_name__, "orientation", str(int(gtk_toolbox.get_screen_orientation())))
605 for i, value in enumerate(self._credentials):
606 blob = base64.b64encode(value)
607 config.set(constants.__pretty_app_name__, "bin_blob_%i" % i, blob)
608 config.add_section("alarm")
609 if self._alarmHandler is not None:
610 self._alarmHandler.save_settings(config, "alarm")
612 for backendId, view in itertools.chain(
613 self._dialpads.iteritems(),
614 self._accountViews.iteritems(),
615 self._messagesViews.iteritems(),
616 self._recentViews.iteritems(),
617 self._contactsViews.iteritems(),
619 sectionName = "%s - %s" % (backendId, view.name())
620 config.add_section(sectionName)
621 view.save_settings(config, sectionName)
623 def _save_settings(self):
625 @note Thread Agnostic
627 config = ConfigParser.SafeConfigParser()
628 self.save_settings(config)
629 with open(constants._user_settings_, "wb") as configFile:
630 config.write(configFile)
632 def _refresh_active_tab(self):
633 pageIndex = self._notebook.get_current_page()
634 if pageIndex == self.CONTACTS_TAB:
635 self._contactsViews[self._selectedBackendId].update(force=True)
636 elif pageIndex == self.RECENT_TAB:
637 self._recentViews[self._selectedBackendId].update(force=True)
638 elif pageIndex == self.MESSAGES_TAB:
639 self._messagesViews[self._selectedBackendId].update(force=True)
641 if pageIndex in (self.RECENT_TAB, self.MESSAGES_TAB):
642 if self._ledHandler is not None:
643 self._ledHandler.off()
645 def _on_close(self, *args, **kwds):
647 if self._osso is not None:
651 self._save_settings()
655 def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData):
657 For shutdown or save_unsaved_data, our only state is cookies and I think the cookie manager handles that for us.
658 For system_inactivity, we have no background tasks to pause
660 @note Hildon specific
664 for backendId in self.BACKENDS:
665 self._phoneBackends[backendId].clear_caches()
666 self._contactsViews[self._selectedBackendId].clear_caches()
669 if save_unsaved_data or shutdown:
670 self._save_settings()
672 self._errorDisplay.push_exception()
674 def _on_connection_change(self, connection, event, magicIdentifier):
676 @note Hildon specific
681 status = event.get_status()
682 error = event.get_error()
683 iap_id = event.get_iap_id()
684 bearer = event.get_bearer_type()
686 if status == conic.STATUS_CONNECTED:
688 self._spawn_attempt_login()
689 elif status == conic.STATUS_DISCONNECTED:
691 self._defaultBackendId = self._selectedBackendId
692 self._change_loggedin_status(self.NULL_BACKEND)
694 self._errorDisplay.push_exception()
696 def _on_window_state_change(self, widget, event, *args):
698 @note Hildon specific
701 if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
702 self._isFullScreen = True
704 self._isFullScreen = False
706 self._errorDisplay.push_exception()
708 def _on_key_press(self, widget, event, *args):
710 @note Hildon specific
712 RETURN_TYPES = (gtk.keysyms.Return, gtk.keysyms.ISO_Enter, gtk.keysyms.KP_Enter)
715 event.keyval == gtk.keysyms.F6 or
716 event.keyval in RETURN_TYPES and event.get_state() & gtk.gdk.CONTROL_MASK
718 if self._isFullScreen:
719 self._window.unfullscreen()
721 self._window.fullscreen()
722 elif event.keyval == ord("l") and event.get_state() & gtk.gdk.CONTROL_MASK:
723 with open(constants._user_logpath_, "r") as f:
724 logLines = f.xreadlines()
725 log = "".join(logLines)
726 self._clipboard.set_text(str(log))
728 self._errorDisplay.push_exception()
730 def _on_clearcookies_clicked(self, *args):
732 self._phoneBackends[self._selectedBackendId].logout()
733 self._accountViews[self._selectedBackendId].clear()
734 self._recentViews[self._selectedBackendId].clear()
735 self._messagesViews[self._selectedBackendId].clear()
736 self._contactsViews[self._selectedBackendId].clear()
737 self._change_loggedin_status(self.NULL_BACKEND)
739 self._spawn_attempt_login(True)
741 self._errorDisplay.push_exception()
743 def _on_notebook_switch_page(self, notebook, page, pageIndex):
745 self._reset_tab_refresh()
747 didRecentUpdate = False
748 didMessagesUpdate = False
750 if pageIndex == self.RECENT_TAB:
751 didRecentUpdate = self._recentViews[self._selectedBackendId].update()
752 elif pageIndex == self.MESSAGES_TAB:
753 didMessagesUpdate = self._messagesViews[self._selectedBackendId].update()
754 elif pageIndex == self.CONTACTS_TAB:
755 self._contactsViews[self._selectedBackendId].update()
756 elif pageIndex == self.ACCOUNT_TAB:
757 self._accountViews[self._selectedBackendId].update()
759 if didRecentUpdate or didMessagesUpdate:
760 if self._ledHandler is not None:
761 self._ledHandler.off()
763 self._errorDisplay.push_exception()
765 def _set_tab_refresh(self, *args):
767 pageIndex = self._notebook.get_current_page()
768 child = self._notebook.get_nth_page(pageIndex)
769 self._notebook.get_tab_label(child).set_text("Refresh?")
771 self._errorDisplay.push_exception()
774 def _reset_tab_refresh(self, *args):
776 pageIndex = self._notebook.get_current_page()
777 child = self._notebook.get_nth_page(pageIndex)
778 self._notebook.get_tab_label(child).set_text(self._originalCurrentLabels[pageIndex])
780 self._errorDisplay.push_exception()
783 def _on_tab_refresh(self, *args):
785 self._refresh_active_tab()
786 self._reset_tab_refresh()
788 self._errorDisplay.push_exception()
791 def _on_sms_clicked(self, number, message):
793 assert number, "No number specified"
794 assert message, "Empty message"
796 loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
799 self._errorDisplay.push_exception()
803 self._errorDisplay.push_message(
804 "Backend link with GoogleVoice is not working, please try again"
810 self._phoneBackends[self._selectedBackendId].send_sms(number, message)
811 hildonize.show_information_banner(self._window, "Sending to %s" % number)
814 self._errorDisplay.push_exception()
817 self._dialpads[self._selectedBackendId].clear()
819 self._errorDisplay.push_exception()
821 def _on_dial_clicked(self, number):
823 assert number, "No number to call"
825 loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
828 self._errorDisplay.push_exception()
832 self._errorDisplay.push_message(
833 "Backend link with GoogleVoice is not working, please try again"
839 assert self._phoneBackends[self._selectedBackendId].get_callback_number() != "", "No callback number specified"
840 self._phoneBackends[self._selectedBackendId].dial(number)
841 hildonize.show_information_banner(self._window, "Calling %s" % number)
844 self._errorDisplay.push_exception()
847 self._dialpads[self._selectedBackendId].clear()
849 self._errorDisplay.push_exception()
851 def _on_menu_refresh(self, *args):
853 self._refresh_active_tab()
855 self._errorDisplay.push_exception()
857 def _on_paste(self, *args):
859 contents = self._clipboard.wait_for_text()
860 if contents is not None:
861 self._dialpads[self._selectedBackendId].set_number(contents)
863 self._errorDisplay.push_exception()
865 def _on_about_activate(self, *args):
867 dlg = gtk.AboutDialog()
868 dlg.set_name(constants.__pretty_app_name__)
869 dlg.set_version("%s-%d" % (constants.__version__, constants.__build__))
870 dlg.set_copyright("Copyright 2008 - LGPL")
871 dlg.set_comments("Dialcentral is a touch screen enhanced interface to your GoogleVoice account. This application is not affiliated with Google in any way")
872 dlg.set_website("http://gc-dialer.garage.maemo.org/")
873 dlg.set_authors(["<z2n@merctech.com>", "Eric Warnke <ericew@gmail.com>", "Ed Page <eopage@byu.net>"])
877 self._errorDisplay.push_exception()
883 failureCount, testCount = doctest.testmod()
885 print "Tests Successful"
892 _lock_file = os.path.join(constants._data_path_, ".lock")
893 #with gtk_toolbox.flock(_lock_file, 0):
894 gtk.gdk.threads_init()
896 if hildonize.IS_HILDON_SUPPORTED:
897 gtk.set_application_name(constants.__pretty_app_name__)
898 handle = Dialcentral()
899 if not PROFILE_STARTUP:
903 class DummyOptions(object):
909 if __name__ == "__main__":
910 logging.basicConfig(level=logging.DEBUG)
912 if len(sys.argv) > 1:
918 if optparse is not None:
919 parser = optparse.OptionParser()
920 parser.add_option("-t", "--test", action="store_true", dest="test", help="Run tests")
921 (commandOptions, commandArgs) = parser.parse_args()
923 commandOptions = DummyOptions()
926 if commandOptions.test: