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")
45 def getmtime_nothrow(path):
47 return os.path.getmtime(path)
52 def display_error_message(msg):
53 error_dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, msg)
55 def close(dialog, response):
57 error_dialog.connect("response", close)
61 class Dialcentral(object):
64 os.path.join(os.path.dirname(__file__), "dialcentral.glade"),
65 os.path.join(os.path.dirname(__file__), "../lib/dialcentral.glade"),
66 '/usr/lib/dialcentral/dialcentral.glade',
76 # 1 Was GrandCentral support so the gap was maintained for compatibility
78 BACKENDS = (NULL_BACKEND, GV_BACKEND)
81 self._initDone = False
82 self._connection = None
84 self._clipboard = gtk.clipboard_get()
86 self._credentials = ("", "")
87 self._selectedBackendId = self.NULL_BACKEND
88 self._defaultBackendId = self.GV_BACKEND
89 self._phoneBackends = None
91 self._accountViews = None
92 self._messagesViews = None
93 self._recentViews = None
94 self._contactsViews = None
95 self._alarmHandler = None
96 self._ledHandler = None
97 self._originalCurrentLabels = []
99 for path in self._glade_files:
100 if os.path.isfile(path):
101 self._widgetTree = gtk.glade.XML(path)
104 display_error_message("Cannot find dialcentral.glade")
108 self._window = self._widgetTree.get_widget("mainWindow")
109 self._notebook = self._widgetTree.get_widget("notebook")
110 self._errorDisplay = gtk_toolbox.ErrorDisplay(self._widgetTree)
111 self._credentialsDialog = gtk_toolbox.LoginWindow(self._widgetTree)
113 self._isFullScreen = False
114 self._app = hildonize.get_app_class()()
115 self._window = hildonize.hildonize_window(self._app, self._window)
116 hildonize.hildonize_text_entry(self._widgetTree.get_widget("usernameentry"))
117 hildonize.hildonize_password_entry(self._widgetTree.get_widget("passwordentry"))
119 for scrollingWidgetName in (
120 'recent_scrolledwindow',
121 'message_scrolledwindow',
122 'contacts_scrolledwindow',
123 "smsMessages_scrolledwindow",
125 scrollingWidget = self._widgetTree.get_widget(scrollingWidgetName)
126 assert scrollingWidget is not None, scrollingWidgetName
127 hildonize.hildonize_scrollwindow(scrollingWidget)
128 for scrollingWidgetName in (
129 "smsMessage_scrolledEntry",
131 scrollingWidget = self._widgetTree.get_widget(scrollingWidgetName)
132 assert scrollingWidget is not None, scrollingWidgetName
133 hildonize.hildonize_scrollwindow_with_viewport(scrollingWidget)
137 "addressbookSelectButton",
141 "callbackSelectButton",
142 "minutesEntryButton",
144 "phoneTypeSelection",
146 button = self._widgetTree.get_widget(buttonName)
147 assert button is not None, buttonName
148 hildonize.set_button_thumb_selectable(button)
150 replacementButtons = [gtk.Button("Test")]
151 menu = hildonize.hildonize_menu(
153 self._widgetTree.get_widget("dialpad_menubar"),
157 self._window.connect("key-press-event", self._on_key_press)
158 self._window.connect("window-state-event", self._on_window_state_change)
159 if not hildonize.IS_HILDON_SUPPORTED:
160 _moduleLogger.warning("No hildonization support")
162 hildonize.set_application_title(self._window, "%s" % constants.__pretty_app_name__)
164 self._window.connect("destroy", self._on_close)
165 self._window.set_default_size(800, 300)
166 self._window.show_all()
168 self._loginSink = gtk_toolbox.threaded_stage(
171 gtk_toolbox.null_sink(),
175 backgroundSetup = threading.Thread(target=self._idle_setup)
176 backgroundSetup.setDaemon(True)
177 backgroundSetup.start()
179 def _idle_setup(self):
181 If something can be done after the UI loads, push it here so it's not blocking the UI
183 # Barebones UI handlers
188 self._phoneBackends = {self.NULL_BACKEND: null_backend.NullDialer()}
189 with gtk_toolbox.gtk_lock():
190 self._dialpads = {self.NULL_BACKEND: null_views.Dialpad(self._widgetTree)}
191 self._accountViews = {self.NULL_BACKEND: null_views.AccountInfo(self._widgetTree)}
192 self._recentViews = {self.NULL_BACKEND: null_views.RecentCallsView(self._widgetTree)}
193 self._messagesViews = {self.NULL_BACKEND: null_views.MessagesView(self._widgetTree)}
194 self._contactsViews = {self.NULL_BACKEND: null_views.ContactsView(self._widgetTree)}
196 self._dialpads[self._selectedBackendId].enable()
197 self._accountViews[self._selectedBackendId].enable()
198 self._recentViews[self._selectedBackendId].enable()
199 self._messagesViews[self._selectedBackendId].enable()
200 self._contactsViews[self._selectedBackendId].enable()
202 with gtk_toolbox.gtk_lock():
203 self._errorDisplay.push_exception()
205 # Setup maemo specifics
209 except (ImportError, OSError):
213 self._osso = osso.Context(constants.__app_name__, constants.__version__, False)
214 device = osso.DeviceState(self._osso)
215 device.set_device_state_callback(self._on_device_state_change, 0)
217 _moduleLogger.warning("No device state support")
221 if alarm_handler.AlarmHandler is not alarm_handler._NoneAlarmHandler:
222 self._alarmHandler = alarm_handler.AlarmHandler()
224 self._alarmHandler = None
225 except (ImportError, OSError):
228 with gtk_toolbox.gtk_lock():
229 self._errorDisplay.push_exception()
231 if alarm_handler is None:
232 _moduleLogger.warning("No notification support")
233 if hildonize.IS_HILDON_SUPPORTED:
236 self._ledHandler = led_handler.LedHandler()
238 _moduleLogger.exception('LED Handling failed: "%s"' % str(e))
239 self._ledHandler = None
241 self._ledHandler = None
245 except (ImportError, OSError):
247 self._connection = None
248 if conic is not None:
249 self._connection = conic.Connection()
250 self._connection.connect("connection-event", self._on_connection_change, constants.__app_magic__)
251 self._connection.request_connection(conic.CONNECT_FLAG_NONE)
253 _moduleLogger.warning("No connection support")
255 with gtk_toolbox.gtk_lock():
256 self._errorDisplay.push_exception()
258 # Setup costly backends
265 os.makedirs(constants._data_path_)
269 gvCookiePath = os.path.join(constants._data_path_, "gv_cookies.txt")
271 self._phoneBackends.update({
272 self.GV_BACKEND: gv_backend.GVDialer(gvCookiePath),
274 with gtk_toolbox.gtk_lock():
275 unifiedDialpad = gv_views.Dialpad(self._widgetTree, self._errorDisplay)
276 self._dialpads.update({
277 self.GV_BACKEND: unifiedDialpad,
279 self._accountViews.update({
280 self.GV_BACKEND: gv_views.AccountInfo(
281 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._alarmHandler, self._errorDisplay
284 self._accountViews[self.GV_BACKEND].save_everything = self._save_settings
285 self._recentViews.update({
286 self.GV_BACKEND: gv_views.RecentCallsView(
287 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
290 self._messagesViews.update({
291 self.GV_BACKEND: gv_views.MessagesView(
292 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
295 self._contactsViews.update({
296 self.GV_BACKEND: gv_views.ContactsView(
297 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
301 fsContactsPath = os.path.join(constants._data_path_, "contacts")
302 fileBackend = file_backend.FilesystemAddressBookFactory(fsContactsPath)
304 self._dialpads[self.GV_BACKEND].number_selected = self._select_action
305 self._recentViews[self.GV_BACKEND].number_selected = self._select_action
306 self._messagesViews[self.GV_BACKEND].number_selected = self._select_action
307 self._contactsViews[self.GV_BACKEND].number_selected = self._select_action
310 self._phoneBackends[self.GV_BACKEND],
313 mergedBook = gv_views.MergedAddressBook(addressBooks, gv_views.MergedAddressBook.advanced_lastname_sorter)
314 self._contactsViews[self.GV_BACKEND].append(mergedBook)
315 self._contactsViews[self.GV_BACKEND].extend(addressBooks)
316 self._contactsViews[self.GV_BACKEND].open_addressbook(*self._contactsViews[self.GV_BACKEND].get_addressbooks().next()[0][0:2])
319 "on_paste": self._on_paste,
320 "on_refresh": self._on_menu_refresh,
321 "on_clearcookies_clicked": self._on_clearcookies_clicked,
322 "on_about_activate": self._on_about_activate,
324 if hildonize.GTK_MENU_USED:
325 self._widgetTree.signal_autoconnect(callbackMapping)
326 self._notebook.connect("switch-page", self._on_notebook_switch_page)
327 self._widgetTree.get_widget("clearcookies").connect("clicked", self._on_clearcookies_clicked)
329 with gtk_toolbox.gtk_lock():
330 self._originalCurrentLabels = [
331 self._notebook.get_tab_label(self._notebook.get_nth_page(pageIndex)).get_text()
332 for pageIndex in xrange(self._notebook.get_n_pages())
334 self._notebookTapHandler = gtk_toolbox.TapOrHold(self._notebook)
335 self._notebookTapHandler.enable()
336 self._notebookTapHandler.on_tap = self._reset_tab_refresh
337 self._notebookTapHandler.on_hold = self._on_tab_refresh
338 self._notebookTapHandler.on_holding = self._set_tab_refresh
339 self._notebookTapHandler.on_cancel = self._reset_tab_refresh
341 self._initDone = True
343 config = ConfigParser.SafeConfigParser()
344 config.read(constants._user_settings_)
345 with gtk_toolbox.gtk_lock():
346 self.load_settings(config)
348 with gtk_toolbox.gtk_lock():
349 self._errorDisplay.push_exception()
351 self._spawn_attempt_login()
353 def _spawn_attempt_login(self, *args):
354 self._loginSink.send(args)
356 def _attempt_login(self, force = False):
358 @note This must be run outside of the UI lock
361 assert self._initDone, "Attempting login before app is fully loaded"
363 serviceId = self.NULL_BACKEND
366 with gtk_toolbox.gtk_lock():
367 banner = hildonize.show_busy_banner_start(self._window, "Logging In...")
369 self.refresh_session()
370 serviceId = self._defaultBackendId
373 _moduleLogger.exception('Session refresh failed with the following message "%s"' % str(e))
375 with gtk_toolbox.gtk_lock():
376 hildonize.show_busy_banner_end(banner)
379 loggedIn, serviceId = self._login_by_user()
381 with gtk_toolbox.gtk_lock():
382 self._change_loggedin_status(serviceId)
384 hildonize.show_information_banner(self._window, "Logged In")
386 hildonize.show_information_banner(self._window, "Login Failed")
388 with gtk_toolbox.gtk_lock():
389 self._errorDisplay.push_exception()
391 def refresh_session(self):
393 @note Thread agnostic
395 assert self._initDone, "Attempting login before app is fully loaded"
399 loggedIn = self._login_by_cookie()
401 loggedIn = self._login_by_settings()
404 raise RuntimeError("Login Failed")
406 def _login_by_cookie(self):
408 @note Thread agnostic
410 loggedIn = self._phoneBackends[self._defaultBackendId].is_authed()
412 _moduleLogger.info("Logged into %r through cookies" % self._phoneBackends[self._defaultBackendId])
415 def _login_by_settings(self):
417 @note Thread agnostic
419 username, password = self._credentials
420 loggedIn = self._phoneBackends[self._defaultBackendId].login(username, password)
422 self._credentials = username, password
423 _moduleLogger.info("Logged into %r through settings" % self._phoneBackends[self._defaultBackendId])
426 def _login_by_user(self):
428 @note This must be run outside of the UI lock
430 loggedIn, (username, password) = False, self._credentials
431 tmpServiceId = self.GV_BACKEND
433 with gtk_toolbox.gtk_lock():
434 credentials = self._credentialsDialog.request_credentials(
435 defaultCredentials = self._credentials
437 if not self._phoneBackends[tmpServiceId].get_callback_number():
438 # subtle reminder to the users to configure things
439 self._notebook.set_current_page(self.ACCOUNT_TAB)
441 banner = hildonize.show_busy_banner_start(self._window, "Logging In...")
443 username, password = credentials
444 loggedIn = self._phoneBackends[tmpServiceId].login(username, password)
446 with gtk_toolbox.gtk_lock():
447 hildonize.show_busy_banner_end(banner)
450 serviceId = tmpServiceId
451 self._credentials = username, password
452 _moduleLogger.info("Logged into %r through user request" % self._phoneBackends[serviceId])
454 # Hint to the user that they are not logged in
455 serviceId = self.NULL_BACKEND
456 self._notebook.set_current_page(self.ACCOUNT_TAB)
458 return loggedIn, serviceId
460 def _select_action(self, action, number, message):
461 self.refresh_session()
463 self._on_dial_clicked(number)
464 elif action == "sms":
465 self._on_sms_clicked(number, message)
467 assert False, "Unknown action: %s" % action
469 def _change_loggedin_status(self, newStatus):
470 oldStatus = self._selectedBackendId
471 if oldStatus == newStatus:
474 self._dialpads[oldStatus].disable()
475 self._accountViews[oldStatus].disable()
476 self._recentViews[oldStatus].disable()
477 self._messagesViews[oldStatus].disable()
478 self._contactsViews[oldStatus].disable()
480 self._dialpads[newStatus].enable()
481 self._accountViews[newStatus].enable()
482 self._recentViews[newStatus].enable()
483 self._messagesViews[newStatus].enable()
484 self._contactsViews[newStatus].enable()
486 if self._phoneBackends[self._selectedBackendId].get_callback_number() is None:
487 self._phoneBackends[self._selectedBackendId].set_sane_callback()
489 self._selectedBackendId = newStatus
491 self._accountViews[self._selectedBackendId].update()
492 self._refresh_active_tab()
494 def load_settings(self, config):
499 self._defaultBackendId = config.getint(constants.__pretty_app_name__, "active")
501 config.get(constants.__pretty_app_name__, "bin_blob_%i" % i)
502 for i in xrange(len(self._credentials))
505 base64.b64decode(blob)
508 self._credentials = tuple(creds)
510 if self._alarmHandler is not None:
511 self._alarmHandler.load_settings(config, "alarm")
512 except ConfigParser.NoOptionError, e:
513 _moduleLogger.exception(
514 "Settings file %s is missing section %s" % (
515 constants._user_settings_,
519 except ConfigParser.NoSectionError, e:
520 _moduleLogger.exception(
521 "Settings file %s is missing section %s" % (
522 constants._user_settings_,
527 for backendId, view in itertools.chain(
528 self._dialpads.iteritems(),
529 self._accountViews.iteritems(),
530 self._messagesViews.iteritems(),
531 self._recentViews.iteritems(),
532 self._contactsViews.iteritems(),
534 sectionName = "%s - %s" % (backendId, view.name())
536 view.load_settings(config, sectionName)
537 except ConfigParser.NoOptionError, e:
538 _moduleLogger.exception(
539 "Settings file %s is missing section %s" % (
540 constants._user_settings_,
544 except ConfigParser.NoSectionError, e:
545 _moduleLogger.exception(
546 "Settings file %s is missing section %s" % (
547 constants._user_settings_,
553 previousOrientation = config.getint(constants.__pretty_app_name__, "orientation")
554 if previousOrientation == gtk.ORIENTATION_HORIZONTAL:
555 hildonize.window_to_landscape(self._window)
556 elif previousOrientation == gtk.ORIENTATION_VERTICAL:
557 hildonize.window_to_portrait(self._window)
558 except ConfigParser.NoOptionError, e:
559 _moduleLogger.exception(
560 "Settings file %s is missing section %s" % (
561 constants._user_settings_,
565 except ConfigParser.NoSectionError, e:
566 _moduleLogger.exception(
567 "Settings file %s is missing section %s" % (
568 constants._user_settings_,
573 def save_settings(self, config):
575 @note Thread Agnostic
577 # Because we now only support GVoice, if there are user credentials,
578 # always assume its using the GVoice backend
579 if self._credentials[0] and self._credentials[1]:
580 backend = self.GV_BACKEND
582 backend = self.NULL_BACKEND
584 config.add_section(constants.__pretty_app_name__)
585 config.set(constants.__pretty_app_name__, "active", str(backend))
586 config.set(constants.__pretty_app_name__, "orientation", str(int(gtk_toolbox.get_screen_orientation())))
587 for i, value in enumerate(self._credentials):
588 blob = base64.b64encode(value)
589 config.set(constants.__pretty_app_name__, "bin_blob_%i" % i, blob)
590 config.add_section("alarm")
591 if self._alarmHandler is not None:
592 self._alarmHandler.save_settings(config, "alarm")
594 for backendId, view in itertools.chain(
595 self._dialpads.iteritems(),
596 self._accountViews.iteritems(),
597 self._messagesViews.iteritems(),
598 self._recentViews.iteritems(),
599 self._contactsViews.iteritems(),
601 sectionName = "%s - %s" % (backendId, view.name())
602 config.add_section(sectionName)
603 view.save_settings(config, sectionName)
605 def _save_settings(self):
607 @note Thread Agnostic
609 config = ConfigParser.SafeConfigParser()
610 self.save_settings(config)
611 with open(constants._user_settings_, "wb") as configFile:
612 config.write(configFile)
614 def _refresh_active_tab(self):
615 pageIndex = self._notebook.get_current_page()
616 if pageIndex == self.CONTACTS_TAB:
617 self._contactsViews[self._selectedBackendId].update(force=True)
618 elif pageIndex == self.RECENT_TAB:
619 self._recentViews[self._selectedBackendId].update(force=True)
620 elif pageIndex == self.MESSAGES_TAB:
621 self._messagesViews[self._selectedBackendId].update(force=True)
623 if pageIndex in (self.RECENT_TAB, self.MESSAGES_TAB):
624 if self._ledHandler is not None:
625 self._ledHandler.off()
627 def _on_close(self, *args, **kwds):
629 if self._osso is not None:
633 self._save_settings()
637 def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData):
639 For shutdown or save_unsaved_data, our only state is cookies and I think the cookie manager handles that for us.
640 For system_inactivity, we have no background tasks to pause
642 @note Hildon specific
646 for backendId in self.BACKENDS:
647 self._phoneBackends[backendId].clear_caches()
648 self._contactsViews[self._selectedBackendId].clear_caches()
651 if save_unsaved_data or shutdown:
652 self._save_settings()
654 self._errorDisplay.push_exception()
656 def _on_connection_change(self, connection, event, magicIdentifier):
658 @note Hildon specific
663 status = event.get_status()
664 error = event.get_error()
665 iap_id = event.get_iap_id()
666 bearer = event.get_bearer_type()
668 if status == conic.STATUS_CONNECTED:
670 self._spawn_attempt_login()
671 elif status == conic.STATUS_DISCONNECTED:
673 self._defaultBackendId = self._selectedBackendId
674 self._change_loggedin_status(self.NULL_BACKEND)
676 self._errorDisplay.push_exception()
678 def _on_window_state_change(self, widget, event, *args):
680 @note Hildon specific
683 if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
684 self._isFullScreen = True
686 self._isFullScreen = False
688 self._errorDisplay.push_exception()
690 def _on_key_press(self, widget, event, *args):
692 @note Hildon specific
694 RETURN_TYPES = (gtk.keysyms.Return, gtk.keysyms.ISO_Enter, gtk.keysyms.KP_Enter)
697 event.keyval == gtk.keysyms.F6 or
698 event.keyval in RETURN_TYPES and event.get_state() & gtk.gdk.CONTROL_MASK
700 if self._isFullScreen:
701 self._window.unfullscreen()
703 self._window.fullscreen()
705 self._errorDisplay.push_exception()
707 def _on_clearcookies_clicked(self, *args):
709 self._phoneBackends[self._selectedBackendId].logout()
710 self._accountViews[self._selectedBackendId].clear()
711 self._recentViews[self._selectedBackendId].clear()
712 self._messagesViews[self._selectedBackendId].clear()
713 self._contactsViews[self._selectedBackendId].clear()
714 self._change_loggedin_status(self.NULL_BACKEND)
716 self._spawn_attempt_login(True)
718 self._errorDisplay.push_exception()
720 def _on_notebook_switch_page(self, notebook, page, pageIndex):
722 self._reset_tab_refresh()
724 didRecentUpdate = False
725 didMessagesUpdate = False
727 if pageIndex == self.RECENT_TAB:
728 didRecentUpdate = self._recentViews[self._selectedBackendId].update()
729 elif pageIndex == self.MESSAGES_TAB:
730 didMessagesUpdate = self._messagesViews[self._selectedBackendId].update()
731 elif pageIndex == self.CONTACTS_TAB:
732 self._contactsViews[self._selectedBackendId].update()
733 elif pageIndex == self.ACCOUNT_TAB:
734 self._accountViews[self._selectedBackendId].update()
736 if didRecentUpdate or didMessagesUpdate:
737 if self._ledHandler is not None:
738 self._ledHandler.off()
740 self._errorDisplay.push_exception()
742 def _set_tab_refresh(self, *args):
744 pageIndex = self._notebook.get_current_page()
745 child = self._notebook.get_nth_page(pageIndex)
746 self._notebook.get_tab_label(child).set_text("Refresh?")
748 self._errorDisplay.push_exception()
751 def _reset_tab_refresh(self, *args):
753 pageIndex = self._notebook.get_current_page()
754 child = self._notebook.get_nth_page(pageIndex)
755 self._notebook.get_tab_label(child).set_text(self._originalCurrentLabels[pageIndex])
757 self._errorDisplay.push_exception()
760 def _on_tab_refresh(self, *args):
762 self._refresh_active_tab()
763 self._reset_tab_refresh()
765 self._errorDisplay.push_exception()
768 def _on_sms_clicked(self, number, message):
770 assert number, "No number specified"
771 assert message, "Empty message"
773 loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
776 self._errorDisplay.push_exception()
780 self._errorDisplay.push_message(
781 "Backend link with GoogleVoice is not working, please try again"
787 self._phoneBackends[self._selectedBackendId].send_sms(number, message)
788 hildonize.show_information_banner(self._window, "Sending to %s" % number)
791 self._errorDisplay.push_exception()
794 self._dialpads[self._selectedBackendId].clear()
796 self._errorDisplay.push_exception()
798 def _on_dial_clicked(self, number):
800 assert number, "No number to call"
802 loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
805 self._errorDisplay.push_exception()
809 self._errorDisplay.push_message(
810 "Backend link with GoogleVoice is not working, please try again"
816 assert self._phoneBackends[self._selectedBackendId].get_callback_number() != "", "No callback number specified"
817 self._phoneBackends[self._selectedBackendId].dial(number)
818 hildonize.show_information_banner(self._window, "Calling %s" % number)
821 self._errorDisplay.push_exception()
824 self._dialpads[self._selectedBackendId].clear()
826 self._errorDisplay.push_exception()
828 def _on_menu_refresh(self, *args):
830 self._refresh_active_tab()
832 self._errorDisplay.push_exception()
834 def _on_paste(self, *args):
836 contents = self._clipboard.wait_for_text()
837 if contents is not None:
838 self._dialpads[self._selectedBackendId].set_number(contents)
840 self._errorDisplay.push_exception()
842 def _on_about_activate(self, *args):
844 dlg = gtk.AboutDialog()
845 dlg.set_name(constants.__pretty_app_name__)
846 dlg.set_version("%s-%d" % (constants.__version__, constants.__build__))
847 dlg.set_copyright("Copyright 2008 - LGPL")
848 dlg.set_comments("Dialcentral is a touch screen enhanced interface to your GoogleVoice account. This application is not affiliated with Google in any way")
849 dlg.set_website("http://gc-dialer.garage.maemo.org/")
850 dlg.set_authors(["<z2n@merctech.com>", "Eric Warnke <ericew@gmail.com>", "Ed Page <edpage@byu.net>"])
854 self._errorDisplay.push_exception()
860 failureCount, testCount = doctest.testmod()
862 print "Tests Successful"
869 _lock_file = os.path.join(constants._data_path_, ".lock")
870 #with gtk_toolbox.flock(_lock_file, 0):
871 gtk.gdk.threads_init()
873 if hildonize.IS_HILDON_SUPPORTED:
874 gtk.set_application_name(constants.__pretty_app_name__)
875 handle = Dialcentral()
879 class DummyOptions(object):
885 if __name__ == "__main__":
886 logging.basicConfig(level=logging.DEBUG)
888 if len(sys.argv) > 1:
894 if optparse is not None:
895 parser = optparse.OptionParser()
896 parser.add_option("-t", "--test", action="store_true", dest="test", help="Run tests")
897 (commandOptions, commandArgs) = parser.parse_args()
899 commandOptions = DummyOptions()
902 if commandOptions.test: