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._recentViews = 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 'recent_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)
139 "addressbookSelectButton",
143 "callbackSelectButton",
144 "minutesEntryButton",
146 "phoneTypeSelection",
148 button = self._widgetTree.get_widget(buttonName)
149 assert button is not None, buttonName
150 hildonize.set_button_thumb_selectable(button)
152 menu = hildonize.hildonize_menu(
154 self._widgetTree.get_widget("dialpad_menubar"),
156 if hildonize.IS_FREMANTLE_SUPPORTED:
157 button= gtk.Button("New Login")
158 button.connect("clicked", self._on_clearcookies_clicked)
161 button= gtk.Button("Refresh")
162 button.connect("clicked", self._on_menu_refresh)
165 self._window.connect("key-press-event", self._on_key_press)
166 self._window.connect("window-state-event", self._on_window_state_change)
167 if not hildonize.IS_HILDON_SUPPORTED:
168 _moduleLogger.warning("No hildonization support")
170 hildonize.set_application_title(self._window, "%s" % constants.__pretty_app_name__)
172 self._window.connect("destroy", self._on_close)
173 self._window.set_default_size(800, 300)
174 self._window.show_all()
176 self._loginSink = gtk_toolbox.threaded_stage(
179 gtk_toolbox.null_sink(),
183 if not PROFILE_STARTUP:
184 backgroundSetup = threading.Thread(target=self._idle_setup)
185 backgroundSetup.setDaemon(True)
186 backgroundSetup.start()
190 def _idle_setup(self):
192 If something can be done after the UI loads, push it here so it's not blocking the UI
194 # Barebones UI handlers
196 from backends import null_backend
199 self._phoneBackends = {self.NULL_BACKEND: null_backend.NullDialer()}
200 with gtk_toolbox.gtk_lock():
201 self._dialpads = {self.NULL_BACKEND: null_views.Dialpad(self._widgetTree)}
202 self._accountViews = {self.NULL_BACKEND: null_views.AccountInfo(self._widgetTree)}
203 self._recentViews = {self.NULL_BACKEND: null_views.RecentCallsView(self._widgetTree)}
204 self._messagesViews = {self.NULL_BACKEND: null_views.MessagesView(self._widgetTree)}
205 self._contactsViews = {self.NULL_BACKEND: null_views.ContactsView(self._widgetTree)}
207 self._dialpads[self._selectedBackendId].enable()
208 self._accountViews[self._selectedBackendId].enable()
209 self._recentViews[self._selectedBackendId].enable()
210 self._messagesViews[self._selectedBackendId].enable()
211 self._contactsViews[self._selectedBackendId].enable()
213 with gtk_toolbox.gtk_lock():
214 self._errorDisplay.push_exception()
216 # Setup maemo specifics
220 except (ImportError, OSError):
223 self._deviceState = None
225 self._osso = osso.Context(constants.__app_name__, constants.__version__, False)
226 self._deviceState = osso.DeviceState(self._osso)
227 self._deviceState.set_device_state_callback(self._on_device_state_change, 0)
229 _moduleLogger.warning("No device state support")
233 if alarm_handler.AlarmHandler is not alarm_handler._NoneAlarmHandler:
234 self._alarmHandler = alarm_handler.AlarmHandler()
236 self._alarmHandler = None
237 except (ImportError, OSError):
240 with gtk_toolbox.gtk_lock():
241 self._errorDisplay.push_exception()
243 if alarm_handler is None:
244 _moduleLogger.warning("No notification support")
245 if hildonize.IS_HILDON_SUPPORTED:
248 self._ledHandler = led_handler.LedHandler()
250 _moduleLogger.exception('LED Handling failed: "%s"' % str(e))
251 self._ledHandler = None
253 self._ledHandler = None
257 except (ImportError, OSError):
259 self._connection = None
260 if conic is not None:
261 self._connection = conic.Connection()
262 self._connection.connect("connection-event", self._on_connection_change, constants.__app_magic__)
263 self._connection.request_connection(conic.CONNECT_FLAG_NONE)
265 _moduleLogger.warning("No connection support")
267 with gtk_toolbox.gtk_lock():
268 self._errorDisplay.push_exception()
270 # Setup costly backends
272 from backends import gv_backend
273 from backends import file_backend
275 from backends import merge_backend
278 os.makedirs(constants._data_path_)
282 gvCookiePath = os.path.join(constants._data_path_, "gv_cookies.txt")
284 self._phoneBackends.update({
285 self.GV_BACKEND: gv_backend.GVDialer(gvCookiePath),
287 with gtk_toolbox.gtk_lock():
288 unifiedDialpad = gv_views.Dialpad(self._widgetTree, self._errorDisplay)
289 self._dialpads.update({
290 self.GV_BACKEND: unifiedDialpad,
292 self._accountViews.update({
293 self.GV_BACKEND: gv_views.AccountInfo(
294 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._alarmHandler, self._errorDisplay
297 self._accountViews[self.GV_BACKEND].save_everything = self._save_settings
298 self._recentViews.update({
299 self.GV_BACKEND: gv_views.RecentCallsView(
300 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
303 self._messagesViews.update({
304 self.GV_BACKEND: gv_views.MessagesView(
305 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
308 self._contactsViews.update({
309 self.GV_BACKEND: gv_views.ContactsView(
310 self._widgetTree, self._phoneBackends[self.GV_BACKEND], self._errorDisplay
314 fsContactsPath = os.path.join(constants._data_path_, "contacts")
315 fileBackend = file_backend.FilesystemAddressBookFactory(fsContactsPath)
317 self._dialpads[self.GV_BACKEND].number_selected = self._select_action
318 self._recentViews[self.GV_BACKEND].number_selected = self._select_action
319 self._messagesViews[self.GV_BACKEND].number_selected = self._select_action
320 self._contactsViews[self.GV_BACKEND].number_selected = self._select_action
323 self._phoneBackends[self.GV_BACKEND],
326 mergedBook = merge_backend.MergedAddressBook(addressBooks, merge_backend.MergedAddressBook.advanced_lastname_sorter)
327 self._contactsViews[self.GV_BACKEND].append(mergedBook)
328 self._contactsViews[self.GV_BACKEND].extend(addressBooks)
329 self._contactsViews[self.GV_BACKEND].open_addressbook(*self._contactsViews[self.GV_BACKEND].get_addressbooks().next()[0][0:2])
332 "on_paste": self._on_paste,
333 "on_refresh": self._on_menu_refresh,
334 "on_clearcookies_clicked": self._on_clearcookies_clicked,
335 "on_about_activate": self._on_about_activate,
337 if hildonize.GTK_MENU_USED:
338 self._widgetTree.signal_autoconnect(callbackMapping)
339 self._notebook.connect("switch-page", self._on_notebook_switch_page)
340 self._widgetTree.get_widget("clearcookies").connect("clicked", self._on_clearcookies_clicked)
342 with gtk_toolbox.gtk_lock():
343 self._originalCurrentLabels = [
344 self._notebook.get_tab_label(self._notebook.get_nth_page(pageIndex)).get_text()
345 for pageIndex in xrange(self._notebook.get_n_pages())
347 self._notebookTapHandler = gtk_toolbox.TapOrHold(self._notebook)
348 self._notebookTapHandler.enable()
349 self._notebookTapHandler.on_tap = self._reset_tab_refresh
350 self._notebookTapHandler.on_hold = self._on_tab_refresh
351 self._notebookTapHandler.on_holding = self._set_tab_refresh
352 self._notebookTapHandler.on_cancel = self._reset_tab_refresh
354 config = ConfigParser.SafeConfigParser()
355 config.read(constants._user_settings_)
356 with gtk_toolbox.gtk_lock():
357 self.load_settings(config)
359 with gtk_toolbox.gtk_lock():
360 self._errorDisplay.push_exception()
362 self._initDone = True
363 self._spawn_attempt_login()
365 def _spawn_attempt_login(self, *args):
366 self._loginSink.send(args)
368 def _attempt_login(self, force = False):
370 @note This must be run outside of the UI lock
373 assert self._initDone, "Attempting login before app is fully loaded"
375 serviceId = self.NULL_BACKEND
377 if not force and self._defaultBackendId != self.NULL_BACKEND:
378 with gtk_toolbox.gtk_lock():
379 banner = hildonize.show_busy_banner_start(self._window, "Logging In...")
381 self.refresh_session()
382 serviceId = self._defaultBackendId
385 _moduleLogger.exception('Session refresh failed with the following message "%s"' % str(e))
387 with gtk_toolbox.gtk_lock():
388 hildonize.show_busy_banner_end(banner)
391 loggedIn, serviceId = self._login_by_user()
393 with gtk_toolbox.gtk_lock():
394 self._change_loggedin_status(serviceId)
396 hildonize.show_information_banner(self._window, "Logged In")
398 hildonize.show_information_banner(self._window, "Login Failed")
399 if not self._phoneBackends[self._defaultBackendId].get_callback_number():
400 # subtle reminder to the users to configure things
401 self._notebook.set_current_page(self.ACCOUNT_TAB)
404 with gtk_toolbox.gtk_lock():
405 self._errorDisplay.push_exception()
407 def refresh_session(self):
409 @note Thread agnostic
411 assert self._initDone, "Attempting login before app is fully loaded"
415 loggedIn = self._login_by_cookie()
417 loggedIn = self._login_by_settings()
420 raise RuntimeError("Login Failed")
422 def _login_by_cookie(self):
424 @note Thread agnostic
428 isQuickLoginPossible = self._phoneBackends[self._defaultBackendId].is_quick_login_possible()
429 if self._credentials != ("", "") and isQuickLoginPossible:
431 loggedIn = self._phoneBackends[self._defaultBackendId].is_authed()
434 _moduleLogger.info("Logged into %r through cookies" % self._phoneBackends[self._defaultBackendId])
436 # If the cookies are bad, scratch them completely
437 self._phoneBackends[self._defaultBackendId].logout()
441 def _login_by_settings(self):
443 @note Thread agnostic
445 if self._credentials == ("", ""):
446 # Don't bother with the settings if they are blank
449 username, password = self._credentials
450 loggedIn = self._phoneBackends[self._defaultBackendId].login(username, password)
452 self._credentials = username, password
453 _moduleLogger.info("Logged into %r through settings" % self._phoneBackends[self._defaultBackendId])
456 def _login_by_user(self):
458 @note This must be run outside of the UI lock
460 loggedIn, (username, password) = False, self._credentials
461 tmpServiceId = self.GV_BACKEND
463 with gtk_toolbox.gtk_lock():
464 credentials = self._credentialsDialog.request_credentials(
465 defaultCredentials = self._credentials
467 banner = hildonize.show_busy_banner_start(self._window, "Logging In...")
469 username, password = credentials
470 loggedIn = self._phoneBackends[tmpServiceId].login(username, password)
472 with gtk_toolbox.gtk_lock():
473 hildonize.show_busy_banner_end(banner)
476 serviceId = tmpServiceId
477 self._credentials = username, password
478 _moduleLogger.info("Logged into %r through user request" % self._phoneBackends[serviceId])
480 # Hint to the user that they are not logged in
481 serviceId = self.NULL_BACKEND
482 self._notebook.set_current_page(self.ACCOUNT_TAB)
484 return loggedIn, serviceId
486 def _select_action(self, action, number, message):
487 self.refresh_session()
489 self._on_dial_clicked(number)
490 elif action == "sms":
491 self._on_sms_clicked(number, message)
493 assert False, "Unknown action: %s" % action
495 def _change_loggedin_status(self, newStatus):
496 oldStatus = self._selectedBackendId
497 if oldStatus == newStatus:
500 self._dialpads[oldStatus].disable()
501 self._accountViews[oldStatus].disable()
502 self._recentViews[oldStatus].disable()
503 self._messagesViews[oldStatus].disable()
504 self._contactsViews[oldStatus].disable()
506 self._dialpads[newStatus].enable()
507 self._accountViews[newStatus].enable()
508 self._recentViews[newStatus].enable()
509 self._messagesViews[newStatus].enable()
510 self._contactsViews[newStatus].enable()
512 self._selectedBackendId = newStatus
514 self._accountViews[self._selectedBackendId].update()
515 self._refresh_active_tab()
517 def load_settings(self, config):
522 if not PROFILE_STARTUP:
523 self._defaultBackendId = config.getint(constants.__pretty_app_name__, "active")
525 self._defaultBackendId = self.NULL_BACKEND
527 config.get(constants.__pretty_app_name__, "bin_blob_%i" % i)
528 for i in xrange(len(self._credentials))
531 base64.b64decode(blob)
534 self._credentials = tuple(creds)
536 if self._alarmHandler is not None:
537 self._alarmHandler.load_settings(config, "alarm")
538 except ConfigParser.NoOptionError, e:
539 _moduleLogger.exception(
540 "Settings file %s is missing section %s" % (
541 constants._user_settings_,
545 except ConfigParser.NoSectionError, e:
546 _moduleLogger.exception(
547 "Settings file %s is missing section %s" % (
548 constants._user_settings_,
553 for backendId, view in itertools.chain(
554 self._dialpads.iteritems(),
555 self._accountViews.iteritems(),
556 self._messagesViews.iteritems(),
557 self._recentViews.iteritems(),
558 self._contactsViews.iteritems(),
560 sectionName = "%s - %s" % (backendId, view.name())
562 view.load_settings(config, sectionName)
563 except ConfigParser.NoOptionError, e:
564 _moduleLogger.exception(
565 "Settings file %s is missing section %s" % (
566 constants._user_settings_,
570 except ConfigParser.NoSectionError, e:
571 _moduleLogger.exception(
572 "Settings file %s is missing section %s" % (
573 constants._user_settings_,
579 previousOrientation = config.getint(constants.__pretty_app_name__, "orientation")
580 if previousOrientation == gtk.ORIENTATION_HORIZONTAL:
581 hildonize.window_to_landscape(self._window)
582 elif previousOrientation == gtk.ORIENTATION_VERTICAL:
583 hildonize.window_to_portrait(self._window)
584 except ConfigParser.NoOptionError, e:
585 _moduleLogger.exception(
586 "Settings file %s is missing section %s" % (
587 constants._user_settings_,
591 except ConfigParser.NoSectionError, e:
592 _moduleLogger.exception(
593 "Settings file %s is missing section %s" % (
594 constants._user_settings_,
599 def save_settings(self, config):
601 @note Thread Agnostic
603 # Because we now only support GVoice, if there are user credentials,
604 # always assume its using the GVoice backend
605 if self._credentials[0] and self._credentials[1]:
606 backend = self.GV_BACKEND
608 backend = self.NULL_BACKEND
610 config.add_section(constants.__pretty_app_name__)
611 config.set(constants.__pretty_app_name__, "active", str(backend))
612 config.set(constants.__pretty_app_name__, "orientation", str(int(gtk_toolbox.get_screen_orientation())))
613 for i, value in enumerate(self._credentials):
614 blob = base64.b64encode(value)
615 config.set(constants.__pretty_app_name__, "bin_blob_%i" % i, blob)
616 config.add_section("alarm")
617 if self._alarmHandler is not None:
618 self._alarmHandler.save_settings(config, "alarm")
620 for backendId, view in itertools.chain(
621 self._dialpads.iteritems(),
622 self._accountViews.iteritems(),
623 self._messagesViews.iteritems(),
624 self._recentViews.iteritems(),
625 self._contactsViews.iteritems(),
627 sectionName = "%s - %s" % (backendId, view.name())
628 config.add_section(sectionName)
629 view.save_settings(config, sectionName)
631 def _save_settings(self):
633 @note Thread Agnostic
635 config = ConfigParser.SafeConfigParser()
636 self.save_settings(config)
637 with open(constants._user_settings_, "wb") as configFile:
638 config.write(configFile)
640 def _refresh_active_tab(self):
641 pageIndex = self._notebook.get_current_page()
642 if pageIndex == self.CONTACTS_TAB:
643 self._contactsViews[self._selectedBackendId].update(force=True)
644 elif pageIndex == self.RECENT_TAB:
645 self._recentViews[self._selectedBackendId].update(force=True)
646 elif pageIndex == self.MESSAGES_TAB:
647 self._messagesViews[self._selectedBackendId].update(force=True)
649 if pageIndex in (self.RECENT_TAB, self.MESSAGES_TAB):
650 if self._ledHandler is not None:
651 self._ledHandler.off()
653 @gtk_toolbox.log_exception(_moduleLogger)
654 def _on_close(self, *args, **kwds):
657 self._save_settings()
660 self._deviceState.close()
661 except AttributeError:
662 pass # Either None or close was removed (in Fremantle)
665 except AttributeError:
666 pass # Either None or close was removed (in Fremantle)
670 def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData):
672 For shutdown or save_unsaved_data, our only state is cookies and I think the cookie manager handles that for us.
673 For system_inactivity, we have no background tasks to pause
675 @note Hildon specific
679 for backendId in self.BACKENDS:
680 self._phoneBackends[backendId].clear_caches()
681 self._contactsViews[self._selectedBackendId].clear_caches()
684 if save_unsaved_data or shutdown:
685 self._save_settings()
687 self._errorDisplay.push_exception()
689 def _on_connection_change(self, connection, event, magicIdentifier):
691 @note Hildon specific
696 status = event.get_status()
697 error = event.get_error()
698 iap_id = event.get_iap_id()
699 bearer = event.get_bearer_type()
701 if status == conic.STATUS_CONNECTED:
703 self._spawn_attempt_login()
704 elif status == conic.STATUS_DISCONNECTED:
706 self._defaultBackendId = self._selectedBackendId
707 self._change_loggedin_status(self.NULL_BACKEND)
709 self._errorDisplay.push_exception()
711 def _on_window_state_change(self, widget, event, *args):
713 @note Hildon specific
716 if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
717 self._isFullScreen = True
719 self._isFullScreen = False
721 self._errorDisplay.push_exception()
723 def _on_key_press(self, widget, event, *args):
725 @note Hildon specific
727 RETURN_TYPES = (gtk.keysyms.Return, gtk.keysyms.ISO_Enter, gtk.keysyms.KP_Enter)
730 event.keyval == gtk.keysyms.F6 or
731 event.keyval in RETURN_TYPES and event.get_state() & gtk.gdk.CONTROL_MASK
733 if self._isFullScreen:
734 self._window.unfullscreen()
736 self._window.fullscreen()
737 elif event.keyval == ord("l") and event.get_state() & gtk.gdk.CONTROL_MASK:
738 with open(constants._user_logpath_, "r") as f:
739 logLines = f.xreadlines()
740 log = "".join(logLines)
741 self._clipboard.set_text(str(log))
742 elif event.keyval == ord("r") and event.get_state() & gtk.gdk.CONTROL_MASK:
743 self._refresh_active_tab()
745 self._errorDisplay.push_exception()
747 def _on_clearcookies_clicked(self, *args):
749 self._phoneBackends[self._selectedBackendId].logout()
750 self._accountViews[self._selectedBackendId].clear()
751 self._recentViews[self._selectedBackendId].clear()
752 self._messagesViews[self._selectedBackendId].clear()
753 self._contactsViews[self._selectedBackendId].clear()
754 self._change_loggedin_status(self.NULL_BACKEND)
756 self._spawn_attempt_login(True)
758 self._errorDisplay.push_exception()
760 def _on_notebook_switch_page(self, notebook, page, pageIndex):
762 self._reset_tab_refresh()
764 didRecentUpdate = False
765 didMessagesUpdate = False
767 if pageIndex == self.RECENT_TAB:
768 didRecentUpdate = self._recentViews[self._selectedBackendId].update()
769 elif pageIndex == self.MESSAGES_TAB:
770 didMessagesUpdate = self._messagesViews[self._selectedBackendId].update()
771 elif pageIndex == self.CONTACTS_TAB:
772 self._contactsViews[self._selectedBackendId].update()
773 elif pageIndex == self.ACCOUNT_TAB:
774 self._accountViews[self._selectedBackendId].update()
776 if didRecentUpdate or didMessagesUpdate:
777 if self._ledHandler is not None:
778 self._ledHandler.off()
780 self._errorDisplay.push_exception()
782 def _set_tab_refresh(self, *args):
784 pageIndex = self._notebook.get_current_page()
785 child = self._notebook.get_nth_page(pageIndex)
786 self._notebook.get_tab_label(child).set_text("Refresh?")
788 self._errorDisplay.push_exception()
791 def _reset_tab_refresh(self, *args):
793 pageIndex = self._notebook.get_current_page()
794 child = self._notebook.get_nth_page(pageIndex)
795 self._notebook.get_tab_label(child).set_text(self._originalCurrentLabels[pageIndex])
797 self._errorDisplay.push_exception()
800 def _on_tab_refresh(self, *args):
802 self._refresh_active_tab()
803 self._reset_tab_refresh()
805 self._errorDisplay.push_exception()
808 def _on_sms_clicked(self, number, message):
810 assert number, "No number specified"
811 assert message, "Empty message"
813 loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
816 self._errorDisplay.push_exception()
820 self._errorDisplay.push_message(
821 "Backend link with GoogleVoice is not working, please try again"
827 self._phoneBackends[self._selectedBackendId].send_sms(number, message)
828 hildonize.show_information_banner(self._window, "Sending to %s" % number)
829 _moduleLogger.info("Sending SMS to %s" % number)
832 self._errorDisplay.push_exception()
835 self._dialpads[self._selectedBackendId].clear()
837 self._errorDisplay.push_exception()
839 def _on_dial_clicked(self, number):
841 assert number, "No number to call"
843 loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
846 self._errorDisplay.push_exception()
850 self._errorDisplay.push_message(
851 "Backend link with GoogleVoice is not working, please try again"
857 assert self._phoneBackends[self._selectedBackendId].get_callback_number() != "", "No callback number specified"
858 self._phoneBackends[self._selectedBackendId].call(number)
859 hildonize.show_information_banner(self._window, "Calling %s" % number)
860 _moduleLogger.info("Calling %s" % number)
863 self._errorDisplay.push_exception()
866 self._dialpads[self._selectedBackendId].clear()
868 self._errorDisplay.push_exception()
870 def _on_menu_refresh(self, *args):
872 self._refresh_active_tab()
874 self._errorDisplay.push_exception()
876 def _on_paste(self, *args):
878 contents = self._clipboard.wait_for_text()
879 if contents is not None:
880 self._dialpads[self._selectedBackendId].set_number(contents)
882 self._errorDisplay.push_exception()
884 def _on_about_activate(self, *args):
886 dlg = gtk.AboutDialog()
887 dlg.set_name(constants.__pretty_app_name__)
888 dlg.set_version("%s-%d" % (constants.__version__, constants.__build__))
889 dlg.set_copyright("Copyright 2008 - LGPL")
890 dlg.set_comments("Dialcentral is a touch screen enhanced interface to your GoogleVoice account. This application is not affiliated with Google in any way")
891 dlg.set_website("http://gc-dialer.garage.maemo.org/")
892 dlg.set_authors(["<z2n@merctech.com>", "Eric Warnke <ericew@gmail.com>", "Ed Page <eopage@byu.net>"])
896 self._errorDisplay.push_exception()
902 failureCount, testCount = doctest.testmod()
904 print "Tests Successful"
911 _lock_file = os.path.join(constants._data_path_, ".lock")
912 #with gtk_toolbox.flock(_lock_file, 0):
913 gtk.gdk.threads_init()
915 if hildonize.IS_HILDON_SUPPORTED:
916 gtk.set_application_name(constants.__pretty_app_name__)
917 handle = Dialcentral()
918 if not PROFILE_STARTUP:
922 class DummyOptions(object):
928 if __name__ == "__main__":
929 logging.basicConfig(level=logging.DEBUG)
931 if len(sys.argv) > 1:
937 if optparse is not None:
938 parser = optparse.OptionParser()
939 parser.add_option("-t", "--test", action="store_true", dest="test", help="Run tests")
940 (commandOptions, commandArgs) = parser.parse_args()
942 commandOptions = DummyOptions()
945 if commandOptions.test: