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 self._alarmHandler = alarm_handler.AlarmHandler()
222 except (ImportError, OSError):
225 with gtk_toolbox.gtk_lock():
226 self._errorDisplay.push_exception()
228 _moduleLogger.warning("No notification support")
229 if hildonize.IS_HILDON_SUPPORTED:
232 self._ledHandler = led_handler.LedHandler()
234 _moduleLogger.exception('LED Handling failed: "%s"' % str(e))
235 self._ledHandler = None
237 self._ledHandler = None
241 except (ImportError, OSError):
243 self._connection = None
244 if conic is not None:
245 self._connection = conic.Connection()
246 self._connection.connect("connection-event", self._on_connection_change, constants.__app_magic__)
247 self._connection.request_connection(conic.CONNECT_FLAG_NONE)
249 _moduleLogger.warning("No connection support")
251 with gtk_toolbox.gtk_lock():
252 self._errorDisplay.push_exception()
254 # Setup costly backends
261 os.makedirs(constants._data_path_)
265 gvCookiePath = os.path.join(constants._data_path_, "gv_cookies.txt")
267 self._phoneBackends.update({
268 self.GV_BACKEND: gv_backend.GVDialer(gvCookiePath),
270 with gtk_toolbox.gtk_lock():
271 unifiedDialpad = gv_views.Dialpad(self._widgetTree, self._errorDisplay)
272 self._dialpads.update({
273 self.GV_BACKEND: unifiedDialpad,
275 self._accountViews.update({
276 self.GV_BACKEND: gv_views.AccountInfo(
277 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._alarmHandler, self._errorDisplay
280 self._accountViews[self.GV_BACKEND].save_everything = self._save_settings
281 self._recentViews.update({
282 self.GV_BACKEND: gv_views.RecentCallsView(
283 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
286 self._messagesViews.update({
287 self.GV_BACKEND: gv_views.MessagesView(
288 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
291 self._contactsViews.update({
292 self.GV_BACKEND: gv_views.ContactsView(
293 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
297 fsContactsPath = os.path.join(constants._data_path_, "contacts")
298 fileBackend = file_backend.FilesystemAddressBookFactory(fsContactsPath)
300 self._dialpads[self.GV_BACKEND].number_selected = self._select_action
301 self._recentViews[self.GV_BACKEND].number_selected = self._select_action
302 self._messagesViews[self.GV_BACKEND].number_selected = self._select_action
303 self._contactsViews[self.GV_BACKEND].number_selected = self._select_action
306 self._phoneBackends[self.GV_BACKEND],
309 mergedBook = gv_views.MergedAddressBook(addressBooks, gv_views.MergedAddressBook.advanced_lastname_sorter)
310 self._contactsViews[self.GV_BACKEND].append(mergedBook)
311 self._contactsViews[self.GV_BACKEND].extend(addressBooks)
312 self._contactsViews[self.GV_BACKEND].open_addressbook(*self._contactsViews[self.GV_BACKEND].get_addressbooks().next()[0][0:2])
315 "on_paste": self._on_paste,
316 "on_refresh": self._on_menu_refresh,
317 "on_clearcookies_clicked": self._on_clearcookies_clicked,
318 "on_about_activate": self._on_about_activate,
320 if hildonize.GTK_MENU_USED:
321 self._widgetTree.signal_autoconnect(callbackMapping)
322 self._notebook.connect("switch-page", self._on_notebook_switch_page)
323 self._widgetTree.get_widget("clearcookies").connect("clicked", self._on_clearcookies_clicked)
325 with gtk_toolbox.gtk_lock():
326 self._originalCurrentLabels = [
327 self._notebook.get_tab_label(self._notebook.get_nth_page(pageIndex)).get_text()
328 for pageIndex in xrange(self._notebook.get_n_pages())
330 self._notebookTapHandler = gtk_toolbox.TapOrHold(self._notebook)
331 self._notebookTapHandler.enable()
332 self._notebookTapHandler.on_tap = self._reset_tab_refresh
333 self._notebookTapHandler.on_hold = self._on_tab_refresh
334 self._notebookTapHandler.on_holding = self._set_tab_refresh
335 self._notebookTapHandler.on_cancel = self._reset_tab_refresh
337 self._initDone = True
339 config = ConfigParser.SafeConfigParser()
340 config.read(constants._user_settings_)
341 with gtk_toolbox.gtk_lock():
342 self.load_settings(config)
344 with gtk_toolbox.gtk_lock():
345 self._errorDisplay.push_exception()
347 self._spawn_attempt_login()
349 def _spawn_attempt_login(self, *args):
350 self._loginSink.send(args)
352 def _attempt_login(self, force = False):
354 @note This must be run outside of the UI lock
357 assert self._initDone, "Attempting login before app is fully loaded"
359 serviceId = self.NULL_BACKEND
362 with gtk_toolbox.gtk_lock():
363 banner = hildonize.show_busy_banner_start(self._window, "Logging In...")
365 self.refresh_session()
366 serviceId = self._defaultBackendId
369 _moduleLogger.exception('Session refresh failed with the following message "%s"' % str(e))
371 with gtk_toolbox.gtk_lock():
372 hildonize.show_busy_banner_end(banner)
375 loggedIn, serviceId = self._login_by_user()
377 with gtk_toolbox.gtk_lock():
378 self._change_loggedin_status(serviceId)
380 hildonize.show_information_banner(self._window, "Logged In")
382 hildonize.show_information_banner(self._window, "Login Failed")
384 with gtk_toolbox.gtk_lock():
385 self._errorDisplay.push_exception()
387 def refresh_session(self):
389 @note Thread agnostic
391 assert self._initDone, "Attempting login before app is fully loaded"
395 loggedIn = self._login_by_cookie()
397 loggedIn = self._login_by_settings()
400 raise RuntimeError("Login Failed")
402 def _login_by_cookie(self):
404 @note Thread agnostic
406 loggedIn = self._phoneBackends[self._defaultBackendId].is_authed()
408 _moduleLogger.info("Logged into %r through cookies" % self._phoneBackends[self._defaultBackendId])
411 def _login_by_settings(self):
413 @note Thread agnostic
415 username, password = self._credentials
416 loggedIn = self._phoneBackends[self._defaultBackendId].login(username, password)
418 self._credentials = username, password
419 _moduleLogger.info("Logged into %r through settings" % self._phoneBackends[self._defaultBackendId])
422 def _login_by_user(self):
424 @note This must be run outside of the UI lock
426 loggedIn, (username, password) = False, self._credentials
427 tmpServiceId = self.GV_BACKEND
429 with gtk_toolbox.gtk_lock():
430 credentials = self._credentialsDialog.request_credentials(
431 defaultCredentials = self._credentials
433 if not self._phoneBackends[tmpServiceId].get_callback_number():
434 # subtle reminder to the users to configure things
435 self._notebook.set_current_page(self.ACCOUNT_TAB)
437 banner = hildonize.show_busy_banner_start(self._window, "Logging In...")
439 username, password = credentials
440 loggedIn = self._phoneBackends[tmpServiceId].login(username, password)
442 with gtk_toolbox.gtk_lock():
443 hildonize.show_busy_banner_end(banner)
446 serviceId = tmpServiceId
447 self._credentials = username, password
448 _moduleLogger.info("Logged into %r through user request" % self._phoneBackends[serviceId])
450 # Hint to the user that they are not logged in
451 serviceId = self.NULL_BACKEND
452 self._notebook.set_current_page(self.ACCOUNT_TAB)
454 return loggedIn, serviceId
456 def _select_action(self, action, number, message):
457 self.refresh_session()
459 self._on_dial_clicked(number)
460 elif action == "sms":
461 self._on_sms_clicked(number, message)
463 assert False, "Unknown action: %s" % action
465 def _change_loggedin_status(self, newStatus):
466 oldStatus = self._selectedBackendId
467 if oldStatus == newStatus:
470 self._dialpads[oldStatus].disable()
471 self._accountViews[oldStatus].disable()
472 self._recentViews[oldStatus].disable()
473 self._messagesViews[oldStatus].disable()
474 self._contactsViews[oldStatus].disable()
476 self._dialpads[newStatus].enable()
477 self._accountViews[newStatus].enable()
478 self._recentViews[newStatus].enable()
479 self._messagesViews[newStatus].enable()
480 self._contactsViews[newStatus].enable()
482 if self._phoneBackends[self._selectedBackendId].get_callback_number() is None:
483 self._phoneBackends[self._selectedBackendId].set_sane_callback()
485 self._selectedBackendId = newStatus
487 self._accountViews[self._selectedBackendId].update()
488 self._refresh_active_tab()
490 def load_settings(self, config):
495 self._defaultBackendId = config.getint(constants.__pretty_app_name__, "active")
497 config.get(constants.__pretty_app_name__, "bin_blob_%i" % i)
498 for i in xrange(len(self._credentials))
501 base64.b64decode(blob)
504 self._credentials = tuple(creds)
506 if self._alarmHandler is not None:
507 self._alarmHandler.load_settings(config, "alarm")
508 except ConfigParser.NoOptionError, e:
509 _moduleLogger.exception(
510 "Settings file %s is missing section %s" % (
511 constants._user_settings_,
515 except ConfigParser.NoSectionError, e:
516 _moduleLogger.exception(
517 "Settings file %s is missing section %s" % (
518 constants._user_settings_,
523 for backendId, view in itertools.chain(
524 self._dialpads.iteritems(),
525 self._accountViews.iteritems(),
526 self._messagesViews.iteritems(),
527 self._recentViews.iteritems(),
528 self._contactsViews.iteritems(),
530 sectionName = "%s - %s" % (backendId, view.name())
532 view.load_settings(config, sectionName)
533 except ConfigParser.NoOptionError, e:
534 _moduleLogger.exception(
535 "Settings file %s is missing section %s" % (
536 constants._user_settings_,
540 except ConfigParser.NoSectionError, e:
541 _moduleLogger.exception(
542 "Settings file %s is missing section %s" % (
543 constants._user_settings_,
549 previousOrientation = config.getint(constants.__pretty_app_name__, "orientation")
550 if previousOrientation == gtk.ORIENTATION_HORIZONTAL:
551 hildonize.window_to_landscape(self._window)
552 elif previousOrientation == gtk.ORIENTATION_VERTICAL:
553 hildonize.window_to_portrait(self._window)
554 except ConfigParser.NoOptionError, e:
555 _moduleLogger.exception(
556 "Settings file %s is missing section %s" % (
557 constants._user_settings_,
561 except ConfigParser.NoSectionError, e:
562 _moduleLogger.exception(
563 "Settings file %s is missing section %s" % (
564 constants._user_settings_,
569 def save_settings(self, config):
571 @note Thread Agnostic
573 # Because we now only support GVoice, if there are user credentials,
574 # always assume its using the GVoice backend
575 if self._credentials[0] and self._credentials[1]:
576 backend = self.GV_BACKEND
578 backend = self.NULL_BACKEND
580 config.add_section(constants.__pretty_app_name__)
581 config.set(constants.__pretty_app_name__, "active", str(backend))
582 config.set(constants.__pretty_app_name__, "orientation", str(int(gtk_toolbox.get_screen_orientation())))
583 for i, value in enumerate(self._credentials):
584 blob = base64.b64encode(value)
585 config.set(constants.__pretty_app_name__, "bin_blob_%i" % i, blob)
586 config.add_section("alarm")
587 if self._alarmHandler is not None:
588 self._alarmHandler.save_settings(config, "alarm")
590 for backendId, view in itertools.chain(
591 self._dialpads.iteritems(),
592 self._accountViews.iteritems(),
593 self._messagesViews.iteritems(),
594 self._recentViews.iteritems(),
595 self._contactsViews.iteritems(),
597 sectionName = "%s - %s" % (backendId, view.name())
598 config.add_section(sectionName)
599 view.save_settings(config, sectionName)
601 def _save_settings(self):
603 @note Thread Agnostic
605 config = ConfigParser.SafeConfigParser()
606 self.save_settings(config)
607 with open(constants._user_settings_, "wb") as configFile:
608 config.write(configFile)
610 def _refresh_active_tab(self):
611 pageIndex = self._notebook.get_current_page()
612 if pageIndex == self.CONTACTS_TAB:
613 self._contactsViews[self._selectedBackendId].update(force=True)
614 elif pageIndex == self.RECENT_TAB:
615 self._recentViews[self._selectedBackendId].update(force=True)
616 elif pageIndex == self.MESSAGES_TAB:
617 self._messagesViews[self._selectedBackendId].update(force=True)
619 if pageIndex in (self.RECENT_TAB, self.MESSAGES_TAB):
620 if self._ledHandler is not None:
621 self._ledHandler.off()
623 def _on_close(self, *args, **kwds):
625 if self._osso is not None:
629 self._save_settings()
633 def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData):
635 For shutdown or save_unsaved_data, our only state is cookies and I think the cookie manager handles that for us.
636 For system_inactivity, we have no background tasks to pause
638 @note Hildon specific
642 for backendId in self.BACKENDS:
643 self._phoneBackends[backendId].clear_caches()
644 self._contactsViews[self._selectedBackendId].clear_caches()
647 if save_unsaved_data or shutdown:
648 self._save_settings()
650 self._errorDisplay.push_exception()
652 def _on_connection_change(self, connection, event, magicIdentifier):
654 @note Hildon specific
659 status = event.get_status()
660 error = event.get_error()
661 iap_id = event.get_iap_id()
662 bearer = event.get_bearer_type()
664 if status == conic.STATUS_CONNECTED:
666 self._spawn_attempt_login()
667 elif status == conic.STATUS_DISCONNECTED:
669 self._defaultBackendId = self._selectedBackendId
670 self._change_loggedin_status(self.NULL_BACKEND)
672 self._errorDisplay.push_exception()
674 def _on_window_state_change(self, widget, event, *args):
676 @note Hildon specific
679 if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
680 self._isFullScreen = True
682 self._isFullScreen = False
684 self._errorDisplay.push_exception()
686 def _on_key_press(self, widget, event, *args):
688 @note Hildon specific
690 RETURN_TYPES = (gtk.keysyms.Return, gtk.keysyms.ISO_Enter, gtk.keysyms.KP_Enter)
693 event.keyval == gtk.keysyms.F6 or
694 event.keyval in RETURN_TYPES and event.get_state() & gtk.gdk.CONTROL_MASK
696 if self._isFullScreen:
697 self._window.unfullscreen()
699 self._window.fullscreen()
701 self._errorDisplay.push_exception()
703 def _on_clearcookies_clicked(self, *args):
705 self._phoneBackends[self._selectedBackendId].logout()
706 self._accountViews[self._selectedBackendId].clear()
707 self._recentViews[self._selectedBackendId].clear()
708 self._messagesViews[self._selectedBackendId].clear()
709 self._contactsViews[self._selectedBackendId].clear()
710 self._change_loggedin_status(self.NULL_BACKEND)
712 self._spawn_attempt_login(True)
714 self._errorDisplay.push_exception()
716 def _on_notebook_switch_page(self, notebook, page, pageIndex):
718 self._reset_tab_refresh()
720 didRecentUpdate = False
721 didMessagesUpdate = False
723 if pageIndex == self.RECENT_TAB:
724 didRecentUpdate = self._recentViews[self._selectedBackendId].update()
725 elif pageIndex == self.MESSAGES_TAB:
726 didMessagesUpdate = self._messagesViews[self._selectedBackendId].update()
727 elif pageIndex == self.CONTACTS_TAB:
728 self._contactsViews[self._selectedBackendId].update()
729 elif pageIndex == self.ACCOUNT_TAB:
730 self._accountViews[self._selectedBackendId].update()
732 if didRecentUpdate or didMessagesUpdate:
733 if self._ledHandler is not None:
734 self._ledHandler.off()
736 self._errorDisplay.push_exception()
738 def _set_tab_refresh(self, *args):
740 pageIndex = self._notebook.get_current_page()
741 child = self._notebook.get_nth_page(pageIndex)
742 self._notebook.get_tab_label(child).set_text("Refresh?")
744 self._errorDisplay.push_exception()
747 def _reset_tab_refresh(self, *args):
749 pageIndex = self._notebook.get_current_page()
750 child = self._notebook.get_nth_page(pageIndex)
751 self._notebook.get_tab_label(child).set_text(self._originalCurrentLabels[pageIndex])
753 self._errorDisplay.push_exception()
756 def _on_tab_refresh(self, *args):
758 self._refresh_active_tab()
759 self._reset_tab_refresh()
761 self._errorDisplay.push_exception()
764 def _on_sms_clicked(self, number, message):
766 assert number, "No number specified"
767 assert message, "Empty message"
769 loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
772 self._errorDisplay.push_exception()
776 self._errorDisplay.push_message(
777 "Backend link with GoogleVoice is not working, please try again"
783 self._phoneBackends[self._selectedBackendId].send_sms(number, message)
784 hildonize.show_information_banner(self._window, "Sending to %s" % number)
787 self._errorDisplay.push_exception()
790 self._dialpads[self._selectedBackendId].clear()
792 self._errorDisplay.push_exception()
794 def _on_dial_clicked(self, number):
796 assert number, "No number to call"
798 loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
801 self._errorDisplay.push_exception()
805 self._errorDisplay.push_message(
806 "Backend link with GoogleVoice is not working, please try again"
812 assert self._phoneBackends[self._selectedBackendId].get_callback_number() != "", "No callback number specified"
813 self._phoneBackends[self._selectedBackendId].dial(number)
814 hildonize.show_information_banner(self._window, "Calling %s" % number)
817 self._errorDisplay.push_exception()
820 self._dialpads[self._selectedBackendId].clear()
822 self._errorDisplay.push_exception()
824 def _on_menu_refresh(self, *args):
826 self._refresh_active_tab()
828 self._errorDisplay.push_exception()
830 def _on_paste(self, *args):
832 contents = self._clipboard.wait_for_text()
833 if contents is not None:
834 self._dialpads[self._selectedBackendId].set_number(contents)
836 self._errorDisplay.push_exception()
838 def _on_about_activate(self, *args):
840 dlg = gtk.AboutDialog()
841 dlg.set_name(constants.__pretty_app_name__)
842 dlg.set_version("%s-%d" % (constants.__version__, constants.__build__))
843 dlg.set_copyright("Copyright 2008 - LGPL")
844 dlg.set_comments("Dialcentral is a touch screen enhanced interface to your GoogleVoice account. This application is not affiliated with Google in any way")
845 dlg.set_website("http://gc-dialer.garage.maemo.org/")
846 dlg.set_authors(["<z2n@merctech.com>", "Eric Warnke <ericew@gmail.com>", "Ed Page <edpage@byu.net>"])
850 self._errorDisplay.push_exception()
856 failureCount, testCount = doctest.testmod()
858 print "Tests Successful"
865 _lock_file = os.path.join(constants._data_path_, ".lock")
866 #with gtk_toolbox.flock(_lock_file, 0):
867 gtk.gdk.threads_init()
869 if hildonize.IS_HILDON_SUPPORTED:
870 gtk.set_application_name(constants.__pretty_app_name__)
871 handle = Dialcentral()
875 class DummyOptions(object):
881 if __name__ == "__main__":
882 logging.basicConfig(level=logging.DEBUG)
884 if len(sys.argv) > 1:
890 if optparse is not None:
891 parser = optparse.OptionParser()
892 parser.add_option("-t", "--test", action="store_true", dest="test", help="Run tests")
893 (commandOptions, commandArgs) = parser.parse_args()
895 commandOptions = DummyOptions()
898 if commandOptions.test: