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",
140 "callbackSelectButton",
141 "minutesEntryButton",
143 "phoneTypeSelection",
145 button = self._widgetTree.get_widget(buttonName)
146 assert button is not None, buttonName
147 hildonize.set_button_thumb_selectable(button)
149 replacementButtons = [gtk.Button("Test")]
150 menu = hildonize.hildonize_menu(
152 self._widgetTree.get_widget("dialpad_menubar"),
156 self._window.connect("key-press-event", self._on_key_press)
157 self._window.connect("window-state-event", self._on_window_state_change)
158 if not hildonize.IS_HILDON_SUPPORTED:
159 _moduleLogger.warning("No hildonization support")
161 hildonize.set_application_title(self._window, "%s" % constants.__pretty_app_name__)
163 self._window.connect("destroy", self._on_close)
164 self._window.set_default_size(800, 300)
165 self._window.show_all()
167 self._loginSink = gtk_toolbox.threaded_stage(
170 gtk_toolbox.null_sink(),
174 backgroundSetup = threading.Thread(target=self._idle_setup)
175 backgroundSetup.setDaemon(True)
176 backgroundSetup.start()
178 def _idle_setup(self):
180 If something can be done after the UI loads, push it here so it's not blocking the UI
182 # Barebones UI handlers
187 self._phoneBackends = {self.NULL_BACKEND: null_backend.NullDialer()}
188 with gtk_toolbox.gtk_lock():
189 self._dialpads = {self.NULL_BACKEND: null_views.Dialpad(self._widgetTree)}
190 self._accountViews = {self.NULL_BACKEND: null_views.AccountInfo(self._widgetTree)}
191 self._recentViews = {self.NULL_BACKEND: null_views.RecentCallsView(self._widgetTree)}
192 self._messagesViews = {self.NULL_BACKEND: null_views.MessagesView(self._widgetTree)}
193 self._contactsViews = {self.NULL_BACKEND: null_views.ContactsView(self._widgetTree)}
195 self._dialpads[self._selectedBackendId].enable()
196 self._accountViews[self._selectedBackendId].enable()
197 self._recentViews[self._selectedBackendId].enable()
198 self._messagesViews[self._selectedBackendId].enable()
199 self._contactsViews[self._selectedBackendId].enable()
201 with gtk_toolbox.gtk_lock():
202 self._errorDisplay.push_exception()
204 # Setup maemo specifics
208 except (ImportError, OSError):
212 self._osso = osso.Context(constants.__app_name__, constants.__version__, False)
213 device = osso.DeviceState(self._osso)
214 device.set_device_state_callback(self._on_device_state_change, 0)
216 _moduleLogger.warning("No device state support")
220 self._alarmHandler = alarm_handler.AlarmHandler()
221 except (ImportError, OSError):
224 with gtk_toolbox.gtk_lock():
225 self._errorDisplay.push_exception()
227 _moduleLogger.warning("No notification support")
228 if hildonize.IS_HILDON_SUPPORTED:
231 self._ledHandler = led_handler.LedHandler()
233 _moduleLogger.exception('LED Handling failed: "%s"' % str(e))
234 self._ledHandler = None
236 self._ledHandler = None
240 except (ImportError, OSError):
242 self._connection = None
243 if conic is not None:
244 self._connection = conic.Connection()
245 self._connection.connect("connection-event", self._on_connection_change, constants.__app_magic__)
246 self._connection.request_connection(conic.CONNECT_FLAG_NONE)
248 _moduleLogger.warning("No connection support")
250 with gtk_toolbox.gtk_lock():
251 self._errorDisplay.push_exception()
253 # Setup costly backends
260 os.makedirs(constants._data_path_)
264 gvCookiePath = os.path.join(constants._data_path_, "gv_cookies.txt")
266 self._phoneBackends.update({
267 self.GV_BACKEND: gv_backend.GVDialer(gvCookiePath),
269 with gtk_toolbox.gtk_lock():
270 unifiedDialpad = gv_views.Dialpad(self._widgetTree, self._errorDisplay)
271 self._dialpads.update({
272 self.GV_BACKEND: unifiedDialpad,
274 self._accountViews.update({
275 self.GV_BACKEND: gv_views.AccountInfo(
276 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._alarmHandler, self._errorDisplay
279 self._accountViews[self.GV_BACKEND].save_everything = self._save_settings
280 self._recentViews.update({
281 self.GV_BACKEND: gv_views.RecentCallsView(
282 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
285 self._messagesViews.update({
286 self.GV_BACKEND: gv_views.MessagesView(
287 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
290 self._contactsViews.update({
291 self.GV_BACKEND: gv_views.ContactsView(
292 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
296 fsContactsPath = os.path.join(constants._data_path_, "contacts")
297 fileBackend = file_backend.FilesystemAddressBookFactory(fsContactsPath)
299 self._dialpads[self.GV_BACKEND].number_selected = self._select_action
300 self._recentViews[self.GV_BACKEND].number_selected = self._select_action
301 self._messagesViews[self.GV_BACKEND].number_selected = self._select_action
302 self._contactsViews[self.GV_BACKEND].number_selected = self._select_action
305 self._phoneBackends[self.GV_BACKEND],
308 mergedBook = gv_views.MergedAddressBook(addressBooks, gv_views.MergedAddressBook.advanced_lastname_sorter)
309 self._contactsViews[self.GV_BACKEND].append(mergedBook)
310 self._contactsViews[self.GV_BACKEND].extend(addressBooks)
311 self._contactsViews[self.GV_BACKEND].open_addressbook(*self._contactsViews[self.GV_BACKEND].get_addressbooks().next()[0][0:2])
314 "on_paste": self._on_paste,
315 "on_refresh": self._on_menu_refresh,
316 "on_clearcookies_clicked": self._on_clearcookies_clicked,
317 "on_about_activate": self._on_about_activate,
319 if hildonize.GTK_MENU_USED:
320 self._widgetTree.signal_autoconnect(callbackMapping)
321 self._notebook.connect("switch-page", self._on_notebook_switch_page)
322 self._widgetTree.get_widget("clearcookies").connect("clicked", self._on_clearcookies_clicked)
324 with gtk_toolbox.gtk_lock():
325 self._originalCurrentLabels = [
326 self._notebook.get_tab_label(self._notebook.get_nth_page(pageIndex)).get_text()
327 for pageIndex in xrange(self._notebook.get_n_pages())
329 self._notebookTapHandler = gtk_toolbox.TapOrHold(self._notebook)
330 self._notebookTapHandler.enable()
331 self._notebookTapHandler.on_tap = self._reset_tab_refresh
332 self._notebookTapHandler.on_hold = self._on_tab_refresh
333 self._notebookTapHandler.on_holding = self._set_tab_refresh
334 self._notebookTapHandler.on_cancel = self._reset_tab_refresh
336 self._initDone = True
338 config = ConfigParser.SafeConfigParser()
339 config.read(constants._user_settings_)
340 with gtk_toolbox.gtk_lock():
341 self.load_settings(config)
343 with gtk_toolbox.gtk_lock():
344 self._errorDisplay.push_exception()
346 self._spawn_attempt_login(2)
348 def _spawn_attempt_login(self, *args):
349 self._loginSink.send(args)
351 def _attempt_login(self, numOfAttempts = 10, force = False):
353 @note This must be run outside of the UI lock
356 assert 0 <= numOfAttempts, "That was pointless having 0 or less login attempts"
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(numOfAttempts)
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, numOfAttempts):
424 @note This must be run outside of the UI lock
426 loggedIn, (username, password) = False, self._credentials
427 tmpServiceId = self.GV_BACKEND
428 for attemptCount in xrange(numOfAttempts):
431 with gtk_toolbox.gtk_lock():
432 credentials = self._credentialsDialog.request_credentials(
433 defaultCredentials = self._credentials
435 if not self._phoneBackends[tmpServiceId].get_callback_number():
436 # subtle reminder to the users to configure things
437 self._notebook.set_current_page(self.ACCOUNT_TAB)
438 banner = hildonize.show_busy_banner_start(self._window, "Logging In...")
440 username, password = credentials
441 loggedIn = self._phoneBackends[tmpServiceId].login(username, password)
443 with gtk_toolbox.gtk_lock():
444 hildonize.show_busy_banner_end(banner)
447 serviceId = tmpServiceId
448 self._credentials = username, password
449 _moduleLogger.info("Logged into %r through user request" % self._phoneBackends[serviceId])
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()
458 if action == "select":
459 self._dialpads[self._selectedBackendId].set_number(number)
460 self._notebook.set_current_page(self.KEYPAD_TAB)
461 elif action == "dial":
462 self._on_dial_clicked(number)
463 elif action == "sms":
464 self._on_sms_clicked(number, message)
466 assert False, "Unknown action: %s" % action
468 def _change_loggedin_status(self, newStatus):
469 oldStatus = self._selectedBackendId
470 if oldStatus == newStatus:
473 self._dialpads[oldStatus].disable()
474 self._accountViews[oldStatus].disable()
475 self._recentViews[oldStatus].disable()
476 self._messagesViews[oldStatus].disable()
477 self._contactsViews[oldStatus].disable()
479 self._dialpads[newStatus].enable()
480 self._accountViews[newStatus].enable()
481 self._recentViews[newStatus].enable()
482 self._messagesViews[newStatus].enable()
483 self._contactsViews[newStatus].enable()
485 if self._phoneBackends[self._selectedBackendId].get_callback_number() is None:
486 self._phoneBackends[self._selectedBackendId].set_sane_callback()
488 self._selectedBackendId = newStatus
490 self._accountViews[self._selectedBackendId].update()
491 self._refresh_active_tab()
493 def load_settings(self, config):
498 self._defaultBackendId = config.getint(constants.__pretty_app_name__, "active")
500 config.get(constants.__pretty_app_name__, "bin_blob_%i" % i)
501 for i in xrange(len(self._credentials))
504 base64.b64decode(blob)
507 self._credentials = tuple(creds)
509 if self._alarmHandler is not None:
510 self._alarmHandler.load_settings(config, "alarm")
511 except ConfigParser.NoOptionError, e:
512 _moduleLogger.exception(
513 "Settings file %s is missing section %s" % (
514 constants._user_settings_,
518 except ConfigParser.NoSectionError, e:
519 _moduleLogger.exception(
520 "Settings file %s is missing section %s" % (
521 constants._user_settings_,
526 for backendId, view in itertools.chain(
527 self._dialpads.iteritems(),
528 self._accountViews.iteritems(),
529 self._messagesViews.iteritems(),
530 self._recentViews.iteritems(),
531 self._contactsViews.iteritems(),
533 sectionName = "%s - %s" % (backendId, view.name())
535 view.load_settings(config, sectionName)
536 except ConfigParser.NoOptionError, e:
537 _moduleLogger.exception(
538 "Settings file %s is missing section %s" % (
539 constants._user_settings_,
543 except ConfigParser.NoSectionError, e:
544 _moduleLogger.exception(
545 "Settings file %s is missing section %s" % (
546 constants._user_settings_,
552 previousOrientation = config.getint(constants.__pretty_app_name__, "orientation")
553 if previousOrientation == gtk.ORIENTATION_HORIZONTAL:
554 hildonize.window_to_landscape(self._window)
555 elif previousOrientation == gtk.ORIENTATION_VERTICAL:
556 hildonize.window_to_portrait(self._window)
557 except ConfigParser.NoOptionError, e:
558 _moduleLogger.exception(
559 "Settings file %s is missing section %s" % (
560 constants._user_settings_,
564 except ConfigParser.NoSectionError, e:
565 _moduleLogger.exception(
566 "Settings file %s is missing section %s" % (
567 constants._user_settings_,
572 def save_settings(self, config):
574 @note Thread Agnostic
576 config.add_section(constants.__pretty_app_name__)
577 config.set(constants.__pretty_app_name__, "active", str(self._selectedBackendId))
578 config.set(constants.__pretty_app_name__, "orientation", str(int(gtk_toolbox.get_screen_orientation())))
579 for i, value in enumerate(self._credentials):
580 blob = base64.b64encode(value)
581 config.set(constants.__pretty_app_name__, "bin_blob_%i" % i, blob)
582 config.add_section("alarm")
583 if self._alarmHandler is not None:
584 self._alarmHandler.save_settings(config, "alarm")
586 for backendId, view in itertools.chain(
587 self._dialpads.iteritems(),
588 self._accountViews.iteritems(),
589 self._messagesViews.iteritems(),
590 self._recentViews.iteritems(),
591 self._contactsViews.iteritems(),
593 sectionName = "%s - %s" % (backendId, view.name())
594 config.add_section(sectionName)
595 view.save_settings(config, sectionName)
597 def _save_settings(self):
599 @note Thread Agnostic
601 config = ConfigParser.SafeConfigParser()
602 self.save_settings(config)
603 with open(constants._user_settings_, "wb") as configFile:
604 config.write(configFile)
606 def _refresh_active_tab(self):
607 pageIndex = self._notebook.get_current_page()
608 if pageIndex == self.CONTACTS_TAB:
609 self._contactsViews[self._selectedBackendId].update(force=True)
610 elif pageIndex == self.RECENT_TAB:
611 self._recentViews[self._selectedBackendId].update(force=True)
612 elif pageIndex == self.MESSAGES_TAB:
613 self._messagesViews[self._selectedBackendId].update(force=True)
615 if pageIndex in (self.RECENT_TAB, self.MESSAGES_TAB):
616 if self._ledHandler is not None:
617 self._ledHandler.off()
619 def _on_close(self, *args, **kwds):
621 if self._osso is not None:
625 self._save_settings()
629 def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData):
631 For shutdown or save_unsaved_data, our only state is cookies and I think the cookie manager handles that for us.
632 For system_inactivity, we have no background tasks to pause
634 @note Hildon specific
638 for backendId in self.BACKENDS:
639 self._phoneBackends[backendId].clear_caches()
640 self._contactsViews[self._selectedBackendId].clear_caches()
643 if save_unsaved_data or shutdown:
644 self._save_settings()
646 self._errorDisplay.push_exception()
648 def _on_connection_change(self, connection, event, magicIdentifier):
650 @note Hildon specific
655 status = event.get_status()
656 error = event.get_error()
657 iap_id = event.get_iap_id()
658 bearer = event.get_bearer_type()
660 if status == conic.STATUS_CONNECTED:
662 self._spawn_attempt_login(2)
663 elif status == conic.STATUS_DISCONNECTED:
665 self._defaultBackendId = self._selectedBackendId
666 self._change_loggedin_status(self.NULL_BACKEND)
668 self._errorDisplay.push_exception()
670 def _on_window_state_change(self, widget, event, *args):
672 @note Hildon specific
675 if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
676 self._isFullScreen = True
678 self._isFullScreen = False
680 self._errorDisplay.push_exception()
682 def _on_key_press(self, widget, event, *args):
684 @note Hildon specific
686 RETURN_TYPES = (gtk.keysyms.Return, gtk.keysyms.ISO_Enter, gtk.keysyms.KP_Enter)
689 event.keyval == gtk.keysyms.F6 or
690 event.keyval in RETURN_TYPES and event.get_state() & gtk.gdk.CONTROL_MASK
692 if self._isFullScreen:
693 self._window.unfullscreen()
695 self._window.fullscreen()
697 self._errorDisplay.push_exception()
699 def _on_clearcookies_clicked(self, *args):
701 self._phoneBackends[self._selectedBackendId].logout()
702 self._accountViews[self._selectedBackendId].clear()
703 self._recentViews[self._selectedBackendId].clear()
704 self._messagesViews[self._selectedBackendId].clear()
705 self._contactsViews[self._selectedBackendId].clear()
706 self._change_loggedin_status(self.NULL_BACKEND)
708 self._spawn_attempt_login(2, True)
710 self._errorDisplay.push_exception()
712 def _on_notebook_switch_page(self, notebook, page, pageIndex):
714 self._reset_tab_refresh()
716 didRecentUpdate = False
717 didMessagesUpdate = False
719 if pageIndex == self.RECENT_TAB:
720 didRecentUpdate = self._recentViews[self._selectedBackendId].update()
721 elif pageIndex == self.MESSAGES_TAB:
722 didMessagesUpdate = self._messagesViews[self._selectedBackendId].update()
723 elif pageIndex == self.CONTACTS_TAB:
724 self._contactsViews[self._selectedBackendId].update()
725 elif pageIndex == self.ACCOUNT_TAB:
726 self._accountViews[self._selectedBackendId].update()
728 if didRecentUpdate or didMessagesUpdate:
729 if self._ledHandler is not None:
730 self._ledHandler.off()
732 self._errorDisplay.push_exception()
734 def _set_tab_refresh(self, *args):
736 pageIndex = self._notebook.get_current_page()
737 child = self._notebook.get_nth_page(pageIndex)
738 self._notebook.get_tab_label(child).set_text("Refresh?")
740 self._errorDisplay.push_exception()
743 def _reset_tab_refresh(self, *args):
745 pageIndex = self._notebook.get_current_page()
746 child = self._notebook.get_nth_page(pageIndex)
747 self._notebook.get_tab_label(child).set_text(self._originalCurrentLabels[pageIndex])
749 self._errorDisplay.push_exception()
752 def _on_tab_refresh(self, *args):
754 self._refresh_active_tab()
755 self._reset_tab_refresh()
757 self._errorDisplay.push_exception()
760 def _on_sms_clicked(self, number, message):
762 assert number, "No number specified"
763 assert message, "Empty message"
765 loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
768 self._errorDisplay.push_exception()
772 self._errorDisplay.push_message(
773 "Backend link with GoogleVoice is not working, please try again"
779 self._phoneBackends[self._selectedBackendId].send_sms(number, message)
780 hildonize.show_information_banner(self._window, "Sending to %s" % number)
783 self._errorDisplay.push_exception()
786 self._dialpads[self._selectedBackendId].clear()
788 self._errorDisplay.push_exception()
790 def _on_dial_clicked(self, number):
792 assert number, "No number to call"
794 loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
797 self._errorDisplay.push_exception()
801 self._errorDisplay.push_message(
802 "Backend link with GoogleVoice is not working, please try again"
808 assert self._phoneBackends[self._selectedBackendId].get_callback_number() != "", "No callback number specified"
809 self._phoneBackends[self._selectedBackendId].dial(number)
810 hildonize.show_information_banner(self._window, "Calling %s" % number)
813 self._errorDisplay.push_exception()
816 self._dialpads[self._selectedBackendId].clear()
818 self._errorDisplay.push_exception()
820 def _on_menu_refresh(self, *args):
822 self._refresh_active_tab()
824 self._errorDisplay.push_exception()
826 def _on_paste(self, *args):
828 contents = self._clipboard.wait_for_text()
829 if contents is not None:
830 self._dialpads[self._selectedBackendId].set_number(contents)
832 self._errorDisplay.push_exception()
834 def _on_about_activate(self, *args):
836 dlg = gtk.AboutDialog()
837 dlg.set_name(constants.__pretty_app_name__)
838 dlg.set_version("%s-%d" % (constants.__version__, constants.__build__))
839 dlg.set_copyright("Copyright 2008 - LGPL")
840 dlg.set_comments("Dialcentral is a touch screen enhanced interface to your GoogleVoice account. This application is not affiliated with Google in any way")
841 dlg.set_website("http://gc-dialer.garage.maemo.org/")
842 dlg.set_authors(["<z2n@merctech.com>", "Eric Warnke <ericew@gmail.com>", "Ed Page <edpage@byu.net>"])
846 self._errorDisplay.push_exception()
852 failureCount, testCount = doctest.testmod()
854 print "Tests Successful"
861 _lock_file = os.path.join(constants._data_path_, ".lock")
862 #with gtk_toolbox.flock(_lock_file, 0):
863 gtk.gdk.threads_init()
865 if hildonize.IS_HILDON_SUPPORTED:
866 gtk.set_application_name(constants.__pretty_app_name__)
867 handle = Dialcentral()
871 class DummyOptions(object):
877 if __name__ == "__main__":
878 logging.basicConfig(level=logging.DEBUG)
880 if len(sys.argv) > 1:
886 if optparse is not None:
887 parser = optparse.OptionParser()
888 parser.add_option("-t", "--test", action="store_true", dest="test", help="Run tests")
889 (commandOptions, commandArgs) = parser.parse_args()
891 commandOptions = DummyOptions()
894 if commandOptions.test: