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',
77 BACKENDS = (NULL_BACKEND, GV_BACKEND)
80 self._initDone = False
81 self._connection = None
83 self._clipboard = gtk.clipboard_get()
85 self._credentials = ("", "")
86 self._selectedBackendId = self.NULL_BACKEND
87 self._defaultBackendId = self.GV_BACKEND
88 self._phoneBackends = None
90 self._accountViews = None
91 self._messagesViews = None
92 self._recentViews = None
93 self._contactsViews = None
94 self._alarmHandler = None
95 self._ledHandler = None
96 self._originalCurrentLabels = []
98 for path in self._glade_files:
99 if os.path.isfile(path):
100 self._widgetTree = gtk.glade.XML(path)
103 display_error_message("Cannot find dialcentral.glade")
107 self._window = self._widgetTree.get_widget("mainWindow")
108 self._notebook = self._widgetTree.get_widget("notebook")
109 self._errorDisplay = gtk_toolbox.ErrorDisplay(self._widgetTree)
110 self._credentialsDialog = gtk_toolbox.LoginWindow(self._widgetTree)
112 self._isFullScreen = False
113 self._app = hildonize.get_app_class()()
114 self._window = hildonize.hildonize_window(self._app, self._window)
115 hildonize.hildonize_text_entry(self._widgetTree.get_widget("usernameentry"))
116 hildonize.hildonize_password_entry(self._widgetTree.get_widget("passwordentry"))
118 for scrollingWidgetName in (
119 'recent_scrolledwindow',
120 'message_scrolledwindow',
121 'contacts_scrolledwindow',
122 "smsMessages_scrolledwindow",
124 scrollingWidget = self._widgetTree.get_widget(scrollingWidgetName)
125 assert scrollingWidget is not None, scrollingWidgetName
126 hildonize.hildonize_scrollwindow(scrollingWidget)
127 for scrollingWidgetName in (
128 "smsMessage_scrolledEntry",
130 scrollingWidget = self._widgetTree.get_widget(scrollingWidgetName)
131 assert scrollingWidget is not None, scrollingWidgetName
132 hildonize.hildonize_scrollwindow_with_viewport(scrollingWidget)
136 "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(2)
349 def _spawn_attempt_login(self, *args):
350 self._loginSink.send(args)
352 def _attempt_login(self, numOfAttempts = 10, force = False):
354 @note This must be run outside of the UI lock
357 assert 0 <= numOfAttempts, "That was pointless having 0 or less login attempts"
358 assert self._initDone, "Attempting login before app is fully loaded"
360 serviceId = self.NULL_BACKEND
363 with gtk_toolbox.gtk_lock():
364 banner = hildonize.show_busy_banner_start(self._window, "Logging In...")
366 self.refresh_session()
367 serviceId = self._defaultBackendId
370 _moduleLogger.exception('Session refresh failed with the following message "%s"' % str(e))
372 with gtk_toolbox.gtk_lock():
373 hildonize.show_busy_banner_end(banner)
376 loggedIn, serviceId = self._login_by_user(numOfAttempts)
378 with gtk_toolbox.gtk_lock():
379 self._change_loggedin_status(serviceId)
381 hildonize.show_information_banner(self._window, "Logged In")
383 hildonize.show_information_banner(self._window, "Login Failed")
385 with gtk_toolbox.gtk_lock():
386 self._errorDisplay.push_exception()
388 def refresh_session(self):
390 @note Thread agnostic
392 assert self._initDone, "Attempting login before app is fully loaded"
396 loggedIn = self._login_by_cookie()
398 loggedIn = self._login_by_settings()
401 raise RuntimeError("Login Failed")
403 def _login_by_cookie(self):
405 @note Thread agnostic
407 loggedIn = self._phoneBackends[self._defaultBackendId].is_authed()
409 _moduleLogger.info("Logged into %r through cookies" % self._phoneBackends[self._defaultBackendId])
412 def _login_by_settings(self):
414 @note Thread agnostic
416 username, password = self._credentials
417 loggedIn = self._phoneBackends[self._defaultBackendId].login(username, password)
419 self._credentials = username, password
420 _moduleLogger.info("Logged into %r through settings" % self._phoneBackends[self._defaultBackendId])
423 def _login_by_user(self, numOfAttempts):
425 @note This must be run outside of the UI lock
427 loggedIn, (username, password) = False, self._credentials
428 tmpServiceId = self.GV_BACKEND
429 for attemptCount in xrange(numOfAttempts):
432 with gtk_toolbox.gtk_lock():
433 credentials = self._credentialsDialog.request_credentials(
434 defaultCredentials = self._credentials
436 if not self._phoneBackends[tmpServiceId].get_callback_number():
437 # subtle reminder to the users to configure things
438 self._notebook.set_current_page(self.ACCOUNT_TAB)
439 banner = hildonize.show_busy_banner_start(self._window, "Logging In...")
441 username, password = credentials
442 loggedIn = self._phoneBackends[tmpServiceId].login(username, password)
444 with gtk_toolbox.gtk_lock():
445 hildonize.show_busy_banner_end(banner)
448 serviceId = tmpServiceId
449 self._credentials = username, password
450 _moduleLogger.info("Logged into %r through user request" % self._phoneBackends[serviceId])
452 serviceId = self.NULL_BACKEND
453 self._notebook.set_current_page(self.ACCOUNT_TAB)
455 return loggedIn, serviceId
457 def _select_action(self, action, number, message):
458 self.refresh_session()
459 if action == "select":
460 self._dialpads[self._selectedBackendId].set_number(number)
461 self._notebook.set_current_page(self.KEYPAD_TAB)
462 elif action == "dial":
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 config.add_section(constants.__pretty_app_name__)
578 config.set(constants.__pretty_app_name__, "active", str(self._selectedBackendId))
579 config.set(constants.__pretty_app_name__, "orientation", str(int(gtk_toolbox.get_screen_orientation())))
580 for i, value in enumerate(self._credentials):
581 blob = base64.b64encode(value)
582 config.set(constants.__pretty_app_name__, "bin_blob_%i" % i, blob)
583 config.add_section("alarm")
584 if self._alarmHandler is not None:
585 self._alarmHandler.save_settings(config, "alarm")
587 for backendId, view in itertools.chain(
588 self._dialpads.iteritems(),
589 self._accountViews.iteritems(),
590 self._messagesViews.iteritems(),
591 self._recentViews.iteritems(),
592 self._contactsViews.iteritems(),
594 sectionName = "%s - %s" % (backendId, view.name())
595 config.add_section(sectionName)
596 view.save_settings(config, sectionName)
598 def _save_settings(self):
600 @note Thread Agnostic
602 config = ConfigParser.SafeConfigParser()
603 self.save_settings(config)
604 with open(constants._user_settings_, "wb") as configFile:
605 config.write(configFile)
607 def _refresh_active_tab(self):
608 pageIndex = self._notebook.get_current_page()
609 if pageIndex == self.CONTACTS_TAB:
610 self._contactsViews[self._selectedBackendId].update(force=True)
611 elif pageIndex == self.RECENT_TAB:
612 self._recentViews[self._selectedBackendId].update(force=True)
613 elif pageIndex == self.MESSAGES_TAB:
614 self._messagesViews[self._selectedBackendId].update(force=True)
616 if pageIndex in (self.RECENT_TAB, self.MESSAGES_TAB):
617 if self._ledHandler is not None:
618 self._ledHandler.off()
620 def _on_close(self, *args, **kwds):
622 if self._osso is not None:
626 self._save_settings()
630 def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData):
632 For shutdown or save_unsaved_data, our only state is cookies and I think the cookie manager handles that for us.
633 For system_inactivity, we have no background tasks to pause
635 @note Hildon specific
639 for backendId in self.BACKENDS:
640 self._phoneBackends[backendId].clear_caches()
641 self._contactsViews[self._selectedBackendId].clear_caches()
644 if save_unsaved_data or shutdown:
645 self._save_settings()
647 self._errorDisplay.push_exception()
649 def _on_connection_change(self, connection, event, magicIdentifier):
651 @note Hildon specific
656 status = event.get_status()
657 error = event.get_error()
658 iap_id = event.get_iap_id()
659 bearer = event.get_bearer_type()
661 if status == conic.STATUS_CONNECTED:
663 self._spawn_attempt_login(2)
664 elif status == conic.STATUS_DISCONNECTED:
666 self._defaultBackendId = self._selectedBackendId
667 self._change_loggedin_status(self.NULL_BACKEND)
669 self._errorDisplay.push_exception()
671 def _on_window_state_change(self, widget, event, *args):
673 @note Hildon specific
676 if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
677 self._isFullScreen = True
679 self._isFullScreen = False
681 self._errorDisplay.push_exception()
683 def _on_key_press(self, widget, event, *args):
685 @note Hildon specific
687 RETURN_TYPES = (gtk.keysyms.Return, gtk.keysyms.ISO_Enter, gtk.keysyms.KP_Enter)
690 event.keyval == gtk.keysyms.F6 or
691 event.keyval in RETURN_TYPES and event.get_state() & gtk.gdk.CONTROL_MASK
693 if self._isFullScreen:
694 self._window.unfullscreen()
696 self._window.fullscreen()
698 self._errorDisplay.push_exception()
700 def _on_clearcookies_clicked(self, *args):
702 self._phoneBackends[self._selectedBackendId].logout()
703 self._accountViews[self._selectedBackendId].clear()
704 self._recentViews[self._selectedBackendId].clear()
705 self._messagesViews[self._selectedBackendId].clear()
706 self._contactsViews[self._selectedBackendId].clear()
707 self._change_loggedin_status(self.NULL_BACKEND)
709 self._spawn_attempt_login(2, True)
711 self._errorDisplay.push_exception()
713 def _on_notebook_switch_page(self, notebook, page, pageIndex):
715 self._reset_tab_refresh()
717 didRecentUpdate = False
718 didMessagesUpdate = False
720 if pageIndex == self.RECENT_TAB:
721 didRecentUpdate = self._recentViews[self._selectedBackendId].update()
722 elif pageIndex == self.MESSAGES_TAB:
723 didMessagesUpdate = self._messagesViews[self._selectedBackendId].update()
724 elif pageIndex == self.CONTACTS_TAB:
725 self._contactsViews[self._selectedBackendId].update()
726 elif pageIndex == self.ACCOUNT_TAB:
727 self._accountViews[self._selectedBackendId].update()
729 if didRecentUpdate or didMessagesUpdate:
730 if self._ledHandler is not None:
731 self._ledHandler.off()
733 self._errorDisplay.push_exception()
735 def _set_tab_refresh(self, *args):
737 pageIndex = self._notebook.get_current_page()
738 child = self._notebook.get_nth_page(pageIndex)
739 self._notebook.get_tab_label(child).set_text("Refresh?")
741 self._errorDisplay.push_exception()
744 def _reset_tab_refresh(self, *args):
746 pageIndex = self._notebook.get_current_page()
747 child = self._notebook.get_nth_page(pageIndex)
748 self._notebook.get_tab_label(child).set_text(self._originalCurrentLabels[pageIndex])
750 self._errorDisplay.push_exception()
753 def _on_tab_refresh(self, *args):
755 self._refresh_active_tab()
756 self._reset_tab_refresh()
758 self._errorDisplay.push_exception()
761 def _on_sms_clicked(self, number, message):
763 assert number, "No number specified"
764 assert message, "Empty message"
766 loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
769 self._errorDisplay.push_exception()
773 self._errorDisplay.push_message(
774 "Backend link with GoogleVoice is not working, please try again"
780 self._phoneBackends[self._selectedBackendId].send_sms(number, message)
781 hildonize.show_information_banner(self._window, "Sending to %s" % number)
784 self._errorDisplay.push_exception()
787 self._dialpads[self._selectedBackendId].clear()
789 self._errorDisplay.push_exception()
791 def _on_dial_clicked(self, number):
793 assert number, "No number to call"
795 loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
798 self._errorDisplay.push_exception()
802 self._errorDisplay.push_message(
803 "Backend link with GoogleVoice is not working, please try again"
809 assert self._phoneBackends[self._selectedBackendId].get_callback_number() != "", "No callback number specified"
810 self._phoneBackends[self._selectedBackendId].dial(number)
811 hildonize.show_information_banner(self._window, "Calling %s" % number)
814 self._errorDisplay.push_exception()
817 self._dialpads[self._selectedBackendId].clear()
819 self._errorDisplay.push_exception()
821 def _on_menu_refresh(self, *args):
823 self._refresh_active_tab()
825 self._errorDisplay.push_exception()
827 def _on_paste(self, *args):
829 contents = self._clipboard.wait_for_text()
830 if contents is not None:
831 self._dialpads[self._selectedBackendId].set_number(contents)
833 self._errorDisplay.push_exception()
835 def _on_about_activate(self, *args):
837 dlg = gtk.AboutDialog()
838 dlg.set_name(constants.__pretty_app_name__)
839 dlg.set_version("%s-%d" % (constants.__version__, constants.__build__))
840 dlg.set_copyright("Copyright 2008 - LGPL")
841 dlg.set_comments("Dialcentral is a touch screen enhanced interface to your GoogleVoice account. This application is not affiliated with Google in any way")
842 dlg.set_website("http://gc-dialer.garage.maemo.org/")
843 dlg.set_authors(["<z2n@merctech.com>", "Eric Warnke <ericew@gmail.com>", "Ed Page <edpage@byu.net>"])
847 self._errorDisplay.push_exception()
853 failureCount, testCount = doctest.testmod()
855 print "Tests Successful"
862 _lock_file = os.path.join(constants._data_path_, ".lock")
863 #with gtk_toolbox.flock(_lock_file, 0):
864 gtk.gdk.threads_init()
866 if hildonize.IS_HILDON_SUPPORTED:
867 gtk.set_application_name(constants.__pretty_app_name__)
868 handle = Dialcentral()
872 class DummyOptions(object):
878 if __name__ == "__main__":
879 logging.basicConfig(level=logging.DEBUG)
881 if len(sys.argv) > 1:
887 if optparse is not None:
888 parser = optparse.OptionParser()
889 parser.add_option("-t", "--test", action="store_true", dest="test", help="Run tests")
890 (commandOptions, commandArgs) = parser.parse_args()
892 commandOptions = DummyOptions()
895 if commandOptions.test: