4 DialCentral - Front end for Google's GoogleVoice service.
5 Copyright (C) 2008 Mark Bergman bergman AT merctech DOT com
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 from __future__ import with_statement
42 _moduleLogger = logging.getLogger("dc_glade")
43 PROFILE_STARTUP = False
46 def getmtime_nothrow(path):
48 return os.path.getmtime(path)
53 def display_error_message(msg):
54 error_dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, msg)
56 def close(dialog, response):
58 error_dialog.connect("response", close)
62 class Dialcentral(object):
65 os.path.join(os.path.dirname(__file__), "dialcentral.glade"),
66 os.path.join(os.path.dirname(__file__), "../lib/dialcentral.glade"),
67 '/usr/lib/dialcentral/dialcentral.glade',
77 # 1 Was GrandCentral support so the gap was maintained for compatibility
79 BACKENDS = (NULL_BACKEND, GV_BACKEND)
82 self._initDone = False
83 self._connection = None
85 self._deviceState = None
86 self._clipboard = gtk.clipboard_get()
88 self._credentials = ("", "")
89 self._selectedBackendId = self.NULL_BACKEND
90 self._defaultBackendId = self.GV_BACKEND
91 self._phoneBackends = None
93 self._accountViews = None
94 self._messagesViews = None
95 self._historyViews = None
96 self._contactsViews = None
97 self._alarmHandler = None
98 self._ledHandler = None
99 self._originalCurrentLabels = []
101 for path in self._glade_files:
102 if os.path.isfile(path):
103 self._widgetTree = gtk.glade.XML(path)
106 display_error_message("Cannot find dialcentral.glade")
110 self._window = self._widgetTree.get_widget("mainWindow")
111 self._notebook = self._widgetTree.get_widget("notebook")
112 self._errorDisplay = gtk_toolbox.ErrorDisplay(self._widgetTree)
113 self._credentialsDialog = gtk_toolbox.LoginWindow(self._widgetTree)
115 self._isFullScreen = False
116 self._app = hildonize.get_app_class()()
117 self._window = hildonize.hildonize_window(self._app, self._window)
118 hildonize.hildonize_text_entry(self._widgetTree.get_widget("usernameentry"))
119 hildonize.hildonize_password_entry(self._widgetTree.get_widget("passwordentry"))
121 for scrollingWidgetName in (
122 'history_scrolledwindow',
123 'message_scrolledwindow',
124 'contacts_scrolledwindow',
125 "smsMessages_scrolledwindow",
127 scrollingWidget = self._widgetTree.get_widget(scrollingWidgetName)
128 assert scrollingWidget is not None, scrollingWidgetName
129 hildonize.hildonize_scrollwindow(scrollingWidget)
130 for scrollingWidgetName in (
131 "smsMessage_scrolledEntry",
133 scrollingWidget = self._widgetTree.get_widget(scrollingWidgetName)
134 assert scrollingWidget is not None, scrollingWidgetName
135 hildonize.hildonize_scrollwindow_with_viewport(scrollingWidget)
136 for pannableTreeViewName in (
139 view = self._widgetTree.get_widget(pannableTreeViewName)
140 assert view is not None, pannableTreeViewName
141 hildonize.pannable_treeview_theme_workaround(scrollingWidget)
145 "addressbookSelectButton",
149 "callbackSelectButton",
150 "minutesEntryButton",
152 "phoneTypeSelection",
154 button = self._widgetTree.get_widget(buttonName)
155 assert button is not None, buttonName
156 hildonize.set_button_thumb_selectable(button)
158 menu = hildonize.hildonize_menu(
160 self._widgetTree.get_widget("dialpad_menubar"),
162 if hildonize.IS_FREMANTLE_SUPPORTED:
163 button = gtk.Button("New Login")
164 button.connect("clicked", self._on_clearcookies_clicked)
167 button= gtk.Button("Refresh")
168 button.connect("clicked", self._on_menu_refresh)
173 self._window.connect("key-press-event", self._on_key_press)
174 self._window.connect("window-state-event", self._on_window_state_change)
175 if not hildonize.IS_HILDON_SUPPORTED:
176 _moduleLogger.warning("No hildonization support")
178 hildonize.set_application_title(self._window, "%s" % constants.__pretty_app_name__)
180 self._window.connect("destroy", self._on_close)
181 self._window.set_default_size(800, 300)
182 self._window.show_all()
184 self._loginSink = gtk_toolbox.threaded_stage(
187 gtk_toolbox.null_sink(),
191 if not PROFILE_STARTUP:
192 backgroundSetup = threading.Thread(target=self._idle_setup)
193 backgroundSetup.setDaemon(True)
194 backgroundSetup.start()
198 def _idle_setup(self):
200 If something can be done after the UI loads, push it here so it's not blocking the UI
202 # Barebones UI handlers
204 from backends import null_backend
207 self._phoneBackends = {self.NULL_BACKEND: null_backend.NullDialer()}
208 with gtk_toolbox.gtk_lock():
209 self._dialpads = {self.NULL_BACKEND: null_views.Dialpad(self._widgetTree)}
210 self._accountViews = {self.NULL_BACKEND: null_views.AccountInfo(self._widgetTree)}
211 self._historyViews = {self.NULL_BACKEND: null_views.CallHistoryView(self._widgetTree)}
212 self._messagesViews = {self.NULL_BACKEND: null_views.MessagesView(self._widgetTree)}
213 self._contactsViews = {self.NULL_BACKEND: null_views.ContactsView(self._widgetTree)}
215 self._dialpads[self._selectedBackendId].enable()
216 self._accountViews[self._selectedBackendId].enable()
217 self._historyViews[self._selectedBackendId].enable()
218 self._messagesViews[self._selectedBackendId].enable()
219 self._contactsViews[self._selectedBackendId].enable()
221 with gtk_toolbox.gtk_lock():
222 self._errorDisplay.push_exception()
224 # Setup maemo specifics
228 except (ImportError, OSError):
231 self._deviceState = None
233 self._osso = osso.Context(constants.__app_name__, constants.__version__, False)
234 self._deviceState = osso.DeviceState(self._osso)
235 self._deviceState.set_device_state_callback(self._on_device_state_change, 0)
237 _moduleLogger.warning("No device state support")
241 if alarm_handler.AlarmHandler is not alarm_handler._NoneAlarmHandler:
242 self._alarmHandler = alarm_handler.AlarmHandler()
244 self._alarmHandler = None
245 except (ImportError, OSError):
248 with gtk_toolbox.gtk_lock():
249 self._errorDisplay.push_exception()
251 if alarm_handler is None:
252 _moduleLogger.warning("No notification support")
253 if hildonize.IS_HILDON_SUPPORTED:
256 self._ledHandler = led_handler.LedHandler()
258 _moduleLogger.exception('LED Handling failed: "%s"' % str(e))
259 self._ledHandler = None
261 self._ledHandler = None
265 except (ImportError, OSError):
267 self._connection = None
268 if conic is not None:
269 self._connection = conic.Connection()
270 self._connection.connect("connection-event", self._on_connection_change, constants.__app_magic__)
271 self._connection.request_connection(conic.CONNECT_FLAG_NONE)
273 _moduleLogger.warning("No connection support")
275 with gtk_toolbox.gtk_lock():
276 self._errorDisplay.push_exception()
278 # Setup costly backends
280 from backends import gv_backend
281 from backends import file_backend
283 from backends import merge_backend
286 os.makedirs(constants._data_path_)
290 gvCookiePath = os.path.join(constants._data_path_, "gv_cookies.txt")
292 self._phoneBackends.update({
293 self.GV_BACKEND: gv_backend.GVDialer(gvCookiePath),
295 with gtk_toolbox.gtk_lock():
296 unifiedDialpad = gv_views.Dialpad(self._widgetTree, self._errorDisplay)
297 self._dialpads.update({
298 self.GV_BACKEND: unifiedDialpad,
300 self._accountViews.update({
301 self.GV_BACKEND: gv_views.AccountInfo(
302 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._alarmHandler, self._errorDisplay
305 self._accountViews[self.GV_BACKEND].save_everything = self._save_settings
306 self._historyViews.update({
307 self.GV_BACKEND: gv_views.CallHistoryView(
308 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
311 self._messagesViews.update({
312 self.GV_BACKEND: gv_views.MessagesView(
313 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
316 self._contactsViews.update({
317 self.GV_BACKEND: gv_views.ContactsView(
318 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
322 fsContactsPath = os.path.join(constants._data_path_, "contacts")
323 fileBackend = file_backend.FilesystemAddressBookFactory(fsContactsPath)
325 self._dialpads[self.GV_BACKEND].number_selected = self._select_action
326 self._historyViews[self.GV_BACKEND].number_selected = self._select_action
327 self._messagesViews[self.GV_BACKEND].number_selected = self._select_action
328 self._contactsViews[self.GV_BACKEND].number_selected = self._select_action
331 self._phoneBackends[self.GV_BACKEND],
334 mergedBook = merge_backend.MergedAddressBook(addressBooks, merge_backend.MergedAddressBook.advanced_lastname_sorter)
335 self._contactsViews[self.GV_BACKEND].append(mergedBook)
336 self._contactsViews[self.GV_BACKEND].extend(addressBooks)
337 self._contactsViews[self.GV_BACKEND].open_addressbook(*self._contactsViews[self.GV_BACKEND].get_addressbooks().next()[0][0:2])
340 "on_paste": self._on_paste,
341 "on_refresh": self._on_menu_refresh,
342 "on_clearcookies_clicked": self._on_clearcookies_clicked,
343 "on_about_activate": self._on_about_activate,
345 if hildonize.GTK_MENU_USED:
346 self._widgetTree.signal_autoconnect(callbackMapping)
347 self._notebook.connect("switch-page", self._on_notebook_switch_page)
348 self._widgetTree.get_widget("clearcookies").connect("clicked", self._on_clearcookies_clicked)
350 with gtk_toolbox.gtk_lock():
351 self._originalCurrentLabels = [
352 self._notebook.get_tab_label(self._notebook.get_nth_page(pageIndex)).get_text()
353 for pageIndex in xrange(self._notebook.get_n_pages())
355 self._notebookTapHandler = gtk_toolbox.TapOrHold(self._notebook)
356 self._notebookTapHandler.enable()
357 self._notebookTapHandler.on_tap = self._reset_tab_refresh
358 self._notebookTapHandler.on_hold = self._on_tab_refresh
359 self._notebookTapHandler.on_holding = self._set_tab_refresh
360 self._notebookTapHandler.on_cancel = self._reset_tab_refresh
362 config = ConfigParser.SafeConfigParser()
363 config.read(constants._user_settings_)
364 with gtk_toolbox.gtk_lock():
365 self.load_settings(config)
367 with gtk_toolbox.gtk_lock():
368 self._errorDisplay.push_exception()
370 self._initDone = True
371 self._spawn_attempt_login()
373 def _spawn_attempt_login(self, *args):
374 self._loginSink.send(args)
376 def _attempt_login(self, force = False):
378 @note This must be run outside of the UI lock
381 assert self._initDone, "Attempting login before app is fully loaded"
383 serviceId = self.NULL_BACKEND
385 if not force and self._defaultBackendId != self.NULL_BACKEND:
386 with gtk_toolbox.gtk_lock():
387 banner = hildonize.show_busy_banner_start(self._window, "Logging In...")
389 self.refresh_session()
390 serviceId = self._defaultBackendId
393 _moduleLogger.exception('Session refresh failed with the following message "%s"' % str(e))
395 with gtk_toolbox.gtk_lock():
396 hildonize.show_busy_banner_end(banner)
399 loggedIn, serviceId = self._login_by_user()
401 with gtk_toolbox.gtk_lock():
402 self._change_loggedin_status(serviceId)
404 hildonize.show_information_banner(self._window, "Logged In")
406 hildonize.show_information_banner(self._window, "Login Failed")
407 if not self._phoneBackends[self._defaultBackendId].get_callback_number():
408 # subtle reminder to the users to configure things
409 self._notebook.set_current_page(self.ACCOUNT_TAB)
412 with gtk_toolbox.gtk_lock():
413 self._errorDisplay.push_exception()
415 def refresh_session(self):
417 @note Thread agnostic
419 assert self._initDone, "Attempting login before app is fully loaded"
423 loggedIn = self._login_by_cookie()
425 loggedIn = self._login_by_settings()
428 raise RuntimeError("Login Failed")
430 def _login_by_cookie(self):
432 @note Thread agnostic
436 isQuickLoginPossible = self._phoneBackends[self._defaultBackendId].is_quick_login_possible()
437 if self._credentials != ("", "") and isQuickLoginPossible:
439 loggedIn = self._phoneBackends[self._defaultBackendId].is_authed()
442 _moduleLogger.info("Logged into %r through cookies" % self._phoneBackends[self._defaultBackendId])
444 # If the cookies are bad, scratch them completely
445 self._phoneBackends[self._defaultBackendId].logout()
449 def _login_by_settings(self):
451 @note Thread agnostic
453 if self._credentials == ("", ""):
454 # Don't bother with the settings if they are blank
457 username, password = self._credentials
458 loggedIn = self._phoneBackends[self._defaultBackendId].login(username, password)
460 self._credentials = username, password
461 _moduleLogger.info("Logged into %r through settings" % self._phoneBackends[self._defaultBackendId])
464 def _login_by_user(self):
466 @note This must be run outside of the UI lock
468 loggedIn, (username, password) = False, self._credentials
469 tmpServiceId = self.GV_BACKEND
471 with gtk_toolbox.gtk_lock():
472 credentials = self._credentialsDialog.request_credentials(
473 defaultCredentials = self._credentials
475 banner = hildonize.show_busy_banner_start(self._window, "Logging In...")
477 username, password = credentials
478 loggedIn = self._phoneBackends[tmpServiceId].login(username, password)
480 with gtk_toolbox.gtk_lock():
481 hildonize.show_busy_banner_end(banner)
484 serviceId = tmpServiceId
485 self._credentials = username, password
486 _moduleLogger.info("Logged into %r through user request" % self._phoneBackends[serviceId])
488 # Hint to the user that they are not logged in
489 serviceId = self.NULL_BACKEND
490 self._notebook.set_current_page(self.ACCOUNT_TAB)
492 return loggedIn, serviceId
494 def _select_action(self, action, number, message):
495 self.refresh_session()
497 self._on_dial_clicked(number)
498 elif action == "sms":
499 self._on_sms_clicked(number, message)
501 assert False, "Unknown action: %s" % action
503 def _change_loggedin_status(self, newStatus):
504 oldStatus = self._selectedBackendId
505 if oldStatus == newStatus:
508 self._dialpads[oldStatus].disable()
509 self._accountViews[oldStatus].disable()
510 self._historyViews[oldStatus].disable()
511 self._messagesViews[oldStatus].disable()
512 self._contactsViews[oldStatus].disable()
514 self._dialpads[newStatus].enable()
515 self._accountViews[newStatus].enable()
516 self._historyViews[newStatus].enable()
517 self._messagesViews[newStatus].enable()
518 self._contactsViews[newStatus].enable()
520 self._selectedBackendId = newStatus
522 self._accountViews[self._selectedBackendId].update()
523 self._refresh_active_tab()
525 def load_settings(self, config):
530 if not PROFILE_STARTUP:
531 self._defaultBackendId = config.getint(constants.__pretty_app_name__, "active")
533 self._defaultBackendId = self.NULL_BACKEND
535 config.get(constants.__pretty_app_name__, "bin_blob_%i" % i)
536 for i in xrange(len(self._credentials))
539 base64.b64decode(blob)
542 self._credentials = tuple(creds)
544 if self._alarmHandler is not None:
545 self._alarmHandler.load_settings(config, "alarm")
546 except ConfigParser.NoOptionError, e:
547 _moduleLogger.exception(
548 "Settings file %s is missing section %s" % (
549 constants._user_settings_,
553 except ConfigParser.NoSectionError, e:
554 _moduleLogger.exception(
555 "Settings file %s is missing section %s" % (
556 constants._user_settings_,
561 for backendId, view in itertools.chain(
562 self._dialpads.iteritems(),
563 self._accountViews.iteritems(),
564 self._messagesViews.iteritems(),
565 self._historyViews.iteritems(),
566 self._contactsViews.iteritems(),
568 sectionName = "%s - %s" % (backendId, view.name())
570 view.load_settings(config, sectionName)
571 except ConfigParser.NoOptionError, e:
572 _moduleLogger.exception(
573 "Settings file %s is missing section %s" % (
574 constants._user_settings_,
578 except ConfigParser.NoSectionError, e:
579 _moduleLogger.exception(
580 "Settings file %s is missing section %s" % (
581 constants._user_settings_,
587 previousOrientation = config.getint(constants.__pretty_app_name__, "orientation")
588 if previousOrientation == gtk.ORIENTATION_HORIZONTAL:
589 hildonize.window_to_landscape(self._window)
590 elif previousOrientation == gtk.ORIENTATION_VERTICAL:
591 hildonize.window_to_portrait(self._window)
592 except ConfigParser.NoOptionError, e:
593 _moduleLogger.exception(
594 "Settings file %s is missing section %s" % (
595 constants._user_settings_,
599 except ConfigParser.NoSectionError, e:
600 _moduleLogger.exception(
601 "Settings file %s is missing section %s" % (
602 constants._user_settings_,
607 def save_settings(self, config):
609 @note Thread Agnostic
611 # Because we now only support GVoice, if there are user credentials,
612 # always assume its using the GVoice backend
613 if self._credentials[0] and self._credentials[1]:
614 backend = self.GV_BACKEND
616 backend = self.NULL_BACKEND
618 config.add_section(constants.__pretty_app_name__)
619 config.set(constants.__pretty_app_name__, "active", str(backend))
620 config.set(constants.__pretty_app_name__, "orientation", str(int(gtk_toolbox.get_screen_orientation())))
621 for i, value in enumerate(self._credentials):
622 blob = base64.b64encode(value)
623 config.set(constants.__pretty_app_name__, "bin_blob_%i" % i, blob)
624 config.add_section("alarm")
625 if self._alarmHandler is not None:
626 self._alarmHandler.save_settings(config, "alarm")
628 for backendId, view in itertools.chain(
629 self._dialpads.iteritems(),
630 self._accountViews.iteritems(),
631 self._messagesViews.iteritems(),
632 self._historyViews.iteritems(),
633 self._contactsViews.iteritems(),
635 sectionName = "%s - %s" % (backendId, view.name())
636 config.add_section(sectionName)
637 view.save_settings(config, sectionName)
639 def _save_settings(self):
641 @note Thread Agnostic
643 config = ConfigParser.SafeConfigParser()
644 self.save_settings(config)
645 with open(constants._user_settings_, "wb") as configFile:
646 config.write(configFile)
648 def _refresh_active_tab(self):
649 pageIndex = self._notebook.get_current_page()
650 if pageIndex == self.CONTACTS_TAB:
651 self._contactsViews[self._selectedBackendId].update(force=True)
652 elif pageIndex == self.RECENT_TAB:
653 self._historyViews[self._selectedBackendId].update(force=True)
654 elif pageIndex == self.MESSAGES_TAB:
655 self._messagesViews[self._selectedBackendId].update(force=True)
657 if pageIndex in (self.RECENT_TAB, self.MESSAGES_TAB):
658 if self._ledHandler is not None:
659 self._ledHandler.off()
661 @gtk_toolbox.log_exception(_moduleLogger)
662 def _on_close(self, *args, **kwds):
665 self._save_settings()
668 self._deviceState.close()
669 except AttributeError:
670 pass # Either None or close was removed (in Fremantle)
673 except AttributeError:
674 pass # Either None or close was removed (in Fremantle)
678 def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData):
680 For shutdown or save_unsaved_data, our only state is cookies and I think the cookie manager handles that for us.
681 For system_inactivity, we have no background tasks to pause
683 @note Hildon specific
687 for backendId in self.BACKENDS:
688 self._phoneBackends[backendId].clear_caches()
689 self._contactsViews[self._selectedBackendId].clear_caches()
692 if save_unsaved_data or shutdown:
693 self._save_settings()
695 self._errorDisplay.push_exception()
697 def _on_connection_change(self, connection, event, magicIdentifier):
699 @note Hildon specific
704 status = event.get_status()
705 error = event.get_error()
706 iap_id = event.get_iap_id()
707 bearer = event.get_bearer_type()
709 if status == conic.STATUS_CONNECTED:
711 self._spawn_attempt_login()
712 elif status == conic.STATUS_DISCONNECTED:
714 self._defaultBackendId = self._selectedBackendId
715 self._change_loggedin_status(self.NULL_BACKEND)
717 self._errorDisplay.push_exception()
719 def _on_window_state_change(self, widget, event, *args):
721 @note Hildon specific
724 if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
725 self._isFullScreen = True
727 self._isFullScreen = False
729 self._errorDisplay.push_exception()
731 def _on_key_press(self, widget, event, *args):
733 @note Hildon specific
735 RETURN_TYPES = (gtk.keysyms.Return, gtk.keysyms.ISO_Enter, gtk.keysyms.KP_Enter)
738 event.keyval == gtk.keysyms.F6 or
739 event.keyval in RETURN_TYPES and event.get_state() & gtk.gdk.CONTROL_MASK
741 if self._isFullScreen:
742 self._window.unfullscreen()
744 self._window.fullscreen()
745 elif event.keyval == ord("l") and event.get_state() & gtk.gdk.CONTROL_MASK:
746 with open(constants._user_logpath_, "r") as f:
747 logLines = f.xreadlines()
748 log = "".join(logLines)
749 self._clipboard.set_text(str(log))
750 elif event.keyval == ord("r") and event.get_state() & gtk.gdk.CONTROL_MASK:
751 self._refresh_active_tab()
753 self._errorDisplay.push_exception()
755 def _on_clearcookies_clicked(self, *args):
757 self._phoneBackends[self._selectedBackendId].logout()
758 self._accountViews[self._selectedBackendId].clear()
759 self._historyViews[self._selectedBackendId].clear()
760 self._messagesViews[self._selectedBackendId].clear()
761 self._contactsViews[self._selectedBackendId].clear()
762 self._change_loggedin_status(self.NULL_BACKEND)
764 self._spawn_attempt_login(True)
766 self._errorDisplay.push_exception()
768 def _on_notebook_switch_page(self, notebook, page, pageIndex):
770 self._reset_tab_refresh()
772 didRecentUpdate = False
773 didMessagesUpdate = False
775 if pageIndex == self.RECENT_TAB:
776 didRecentUpdate = self._historyViews[self._selectedBackendId].update()
777 elif pageIndex == self.MESSAGES_TAB:
778 didMessagesUpdate = self._messagesViews[self._selectedBackendId].update()
779 elif pageIndex == self.CONTACTS_TAB:
780 self._contactsViews[self._selectedBackendId].update()
781 elif pageIndex == self.ACCOUNT_TAB:
782 self._accountViews[self._selectedBackendId].update()
784 if didRecentUpdate or didMessagesUpdate:
785 if self._ledHandler is not None:
786 self._ledHandler.off()
788 self._errorDisplay.push_exception()
790 def _set_tab_refresh(self, *args):
792 pageIndex = self._notebook.get_current_page()
793 child = self._notebook.get_nth_page(pageIndex)
794 self._notebook.get_tab_label(child).set_text("Refresh?")
796 self._errorDisplay.push_exception()
799 def _reset_tab_refresh(self, *args):
801 pageIndex = self._notebook.get_current_page()
802 child = self._notebook.get_nth_page(pageIndex)
803 self._notebook.get_tab_label(child).set_text(self._originalCurrentLabels[pageIndex])
805 self._errorDisplay.push_exception()
808 def _on_tab_refresh(self, *args):
810 self._refresh_active_tab()
811 self._reset_tab_refresh()
813 self._errorDisplay.push_exception()
816 def _on_sms_clicked(self, number, message):
818 assert number, "No number specified"
819 assert message, "Empty message"
821 loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
824 self._errorDisplay.push_exception()
828 self._errorDisplay.push_message(
829 "Backend link with GoogleVoice is not working, please try again"
835 self._phoneBackends[self._selectedBackendId].send_sms(number, message)
836 hildonize.show_information_banner(self._window, "Sending to %s" % number)
837 _moduleLogger.info("Sending SMS to %s" % number)
840 self._errorDisplay.push_exception()
843 self._dialpads[self._selectedBackendId].clear()
845 self._errorDisplay.push_exception()
847 def _on_dial_clicked(self, number):
849 assert number, "No number to call"
851 loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
854 self._errorDisplay.push_exception()
858 self._errorDisplay.push_message(
859 "Backend link with GoogleVoice is not working, please try again"
865 assert self._phoneBackends[self._selectedBackendId].get_callback_number() != "", "No callback number specified"
866 self._phoneBackends[self._selectedBackendId].call(number)
867 hildonize.show_information_banner(self._window, "Calling %s" % number)
868 _moduleLogger.info("Calling %s" % number)
871 self._errorDisplay.push_exception()
874 self._dialpads[self._selectedBackendId].clear()
876 self._errorDisplay.push_exception()
878 def _on_menu_refresh(self, *args):
880 self._refresh_active_tab()
882 self._errorDisplay.push_exception()
884 def _on_paste(self, *args):
886 contents = self._clipboard.wait_for_text()
887 if contents is not None:
888 self._dialpads[self._selectedBackendId].set_number(contents)
890 self._errorDisplay.push_exception()
892 def _on_about_activate(self, *args):
894 dlg = gtk.AboutDialog()
895 dlg.set_name(constants.__pretty_app_name__)
896 dlg.set_version("%s-%d" % (constants.__version__, constants.__build__))
897 dlg.set_copyright("Copyright 2008 - LGPL")
898 dlg.set_comments("Dialcentral is a touch screen enhanced interface to your GoogleVoice account. This application is not affiliated with Google in any way")
899 dlg.set_website("http://gc-dialer.garage.maemo.org/")
900 dlg.set_authors(["<z2n@merctech.com>", "Eric Warnke <ericew@gmail.com>", "Ed Page <eopage@byu.net>"])
904 self._errorDisplay.push_exception()
910 failureCount, testCount = doctest.testmod()
912 print "Tests Successful"
919 _lock_file = os.path.join(constants._data_path_, ".lock")
920 #with gtk_toolbox.flock(_lock_file, 0):
921 gtk.gdk.threads_init()
923 if hildonize.IS_HILDON_SUPPORTED:
924 gtk.set_application_name(constants.__pretty_app_name__)
925 handle = Dialcentral()
926 if not PROFILE_STARTUP:
930 class DummyOptions(object):
936 if __name__ == "__main__":
937 logging.basicConfig(level=logging.DEBUG)
939 if len(sys.argv) > 1:
945 if optparse is not None:
946 parser = optparse.OptionParser()
947 parser.add_option("-t", "--test", action="store_true", dest="test", help="Run tests")
948 (commandOptions, commandArgs) = parser.parse_args()
950 commandOptions = DummyOptions()
953 if commandOptions.test: