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
43 _moduleLogger = logging.getLogger("dc_glade")
44 PROFILE_STARTUP = False
47 def getmtime_nothrow(path):
49 return os.path.getmtime(path)
54 def display_error_message(msg):
55 error_dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, msg)
57 def close(dialog, response):
59 error_dialog.connect("response", close)
63 class Dialcentral(object):
66 os.path.join(os.path.dirname(__file__), "dialcentral.glade"),
67 os.path.join(os.path.dirname(__file__), "../lib/dialcentral.glade"),
68 '/usr/lib/dialcentral/dialcentral.glade',
78 # 1 Was GrandCentral support so the gap was maintained for compatibility
80 BACKENDS = (NULL_BACKEND, GV_BACKEND)
83 self._initDone = False
84 self._connection = None
86 self._deviceState = None
87 self._clipboard = gtk.clipboard_get()
89 self._credentials = ("", "")
90 self._selectedBackendId = self.NULL_BACKEND
91 self._defaultBackendId = self.GV_BACKEND
92 self._phoneBackends = None
94 self._accountViews = None
95 self._messagesViews = None
96 self._historyViews = None
97 self._contactsViews = None
98 self._alarmHandler = None
99 self._ledHandler = None
100 self._originalCurrentLabels = []
101 self._fsContactsPath = os.path.join(constants._data_path_, "contacts")
103 for path in self._glade_files:
104 if os.path.isfile(path):
105 self._widgetTree = gtk.glade.XML(path)
108 display_error_message("Cannot find dialcentral.glade")
112 self._window = self._widgetTree.get_widget("mainWindow")
113 self._notebook = self._widgetTree.get_widget("notebook")
114 self._errorDisplay = gtk_toolbox.ErrorDisplay(self._widgetTree)
115 self._credentialsDialog = gtk_toolbox.LoginWindow(self._widgetTree)
117 self._isFullScreen = False
118 self._app = hildonize.get_app_class()()
119 self._window = hildonize.hildonize_window(self._app, self._window)
120 hildonize.hildonize_text_entry(self._widgetTree.get_widget("usernameentry"))
121 hildonize.hildonize_password_entry(self._widgetTree.get_widget("passwordentry"))
123 for scrollingWidgetName in (
124 'history_scrolledwindow',
125 'message_scrolledwindow',
126 'contacts_scrolledwindow',
127 "smsMessages_scrolledwindow",
129 scrollingWidget = self._widgetTree.get_widget(scrollingWidgetName)
130 assert scrollingWidget is not None, scrollingWidgetName
131 hildonize.hildonize_scrollwindow(scrollingWidget)
132 for scrollingWidgetName in (
133 "smsMessage_scrolledEntry",
135 scrollingWidget = self._widgetTree.get_widget(scrollingWidgetName)
136 assert scrollingWidget is not None, scrollingWidgetName
137 hildonize.hildonize_scrollwindow_with_viewport(scrollingWidget)
141 "addressbookSelectButton",
145 "callbackSelectButton",
146 "minutesEntryButton",
148 "phoneTypeSelection",
150 button = self._widgetTree.get_widget(buttonName)
151 assert button is not None, buttonName
152 hildonize.set_button_thumb_selectable(button)
154 menu = hildonize.hildonize_menu(
156 self._widgetTree.get_widget("dialpad_menubar"),
158 if not hildonize.GTK_MENU_USED:
159 button = gtk.Button("New Login")
160 button.connect("clicked", self._on_clearcookies_clicked)
163 button= gtk.Button("Import Contacts")
164 button.connect("clicked", self._on_contact_import)
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 fileBackend = file_backend.FilesystemAddressBookFactory(self._fsContactsPath)
324 self._dialpads[self.GV_BACKEND].number_selected = self._select_action
325 self._historyViews[self.GV_BACKEND].number_selected = self._select_action
326 self._messagesViews[self.GV_BACKEND].number_selected = self._select_action
327 self._contactsViews[self.GV_BACKEND].number_selected = self._select_action
330 self._phoneBackends[self.GV_BACKEND],
333 mergedBook = merge_backend.MergedAddressBook(addressBooks, merge_backend.MergedAddressBook.basic_firtname_sorter)
334 self._contactsViews[self.GV_BACKEND].append(mergedBook)
335 self._contactsViews[self.GV_BACKEND].extend(addressBooks)
336 self._contactsViews[self.GV_BACKEND].open_addressbook(*self._contactsViews[self.GV_BACKEND].get_addressbooks().next()[0][0:2])
339 "on_paste": self._on_paste,
340 "on_refresh": self._on_menu_refresh,
341 "on_clearcookies_clicked": self._on_clearcookies_clicked,
342 "on_about_activate": self._on_about_activate,
343 "on_import": self._on_contact_import,
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")
547 isFullscreen = config.getboolean(constants.__pretty_app_name__, "fullscreen")
549 self._window.fullscreen()
550 except ConfigParser.NoOptionError, e:
551 _moduleLogger.exception(
552 "Settings file %s is missing section %s" % (
553 constants._user_settings_,
557 except ConfigParser.NoSectionError, e:
558 _moduleLogger.exception(
559 "Settings file %s is missing section %s" % (
560 constants._user_settings_,
565 for backendId, view in itertools.chain(
566 self._dialpads.iteritems(),
567 self._accountViews.iteritems(),
568 self._messagesViews.iteritems(),
569 self._historyViews.iteritems(),
570 self._contactsViews.iteritems(),
572 sectionName = "%s - %s" % (backendId, view.name())
574 view.load_settings(config, sectionName)
575 except ConfigParser.NoOptionError, e:
576 _moduleLogger.exception(
577 "Settings file %s is missing section %s" % (
578 constants._user_settings_,
582 except ConfigParser.NoSectionError, e:
583 _moduleLogger.exception(
584 "Settings file %s is missing section %s" % (
585 constants._user_settings_,
591 previousOrientation = config.getint(constants.__pretty_app_name__, "orientation")
592 if previousOrientation == gtk.ORIENTATION_HORIZONTAL:
593 hildonize.window_to_landscape(self._window)
594 elif previousOrientation == gtk.ORIENTATION_VERTICAL:
595 hildonize.window_to_portrait(self._window)
596 except ConfigParser.NoOptionError, e:
597 _moduleLogger.exception(
598 "Settings file %s is missing section %s" % (
599 constants._user_settings_,
603 except ConfigParser.NoSectionError, e:
604 _moduleLogger.exception(
605 "Settings file %s is missing section %s" % (
606 constants._user_settings_,
611 def save_settings(self, config):
613 @note Thread Agnostic
615 # Because we now only support GVoice, if there are user credentials,
616 # always assume its using the GVoice backend
617 if self._credentials[0] and self._credentials[1]:
618 backend = self.GV_BACKEND
620 backend = self.NULL_BACKEND
622 config.add_section(constants.__pretty_app_name__)
623 config.set(constants.__pretty_app_name__, "active", str(backend))
624 config.set(constants.__pretty_app_name__, "orientation", str(int(gtk_toolbox.get_screen_orientation())))
625 config.set(constants.__pretty_app_name__, "fullscreen", str(self._isFullScreen))
626 for i, value in enumerate(self._credentials):
627 blob = base64.b64encode(value)
628 config.set(constants.__pretty_app_name__, "bin_blob_%i" % i, blob)
629 config.add_section("alarm")
630 if self._alarmHandler is not None:
631 self._alarmHandler.save_settings(config, "alarm")
633 for backendId, view in itertools.chain(
634 self._dialpads.iteritems(),
635 self._accountViews.iteritems(),
636 self._messagesViews.iteritems(),
637 self._historyViews.iteritems(),
638 self._contactsViews.iteritems(),
640 sectionName = "%s - %s" % (backendId, view.name())
641 config.add_section(sectionName)
642 view.save_settings(config, sectionName)
644 def _save_settings(self):
646 @note Thread Agnostic
648 config = ConfigParser.SafeConfigParser()
649 self.save_settings(config)
650 with open(constants._user_settings_, "wb") as configFile:
651 config.write(configFile)
653 def _refresh_active_tab(self):
654 pageIndex = self._notebook.get_current_page()
655 if pageIndex == self.CONTACTS_TAB:
656 self._contactsViews[self._selectedBackendId].update(force=True)
657 elif pageIndex == self.RECENT_TAB:
658 self._historyViews[self._selectedBackendId].update(force=True)
659 elif pageIndex == self.MESSAGES_TAB:
660 self._messagesViews[self._selectedBackendId].update(force=True)
662 if pageIndex in (self.RECENT_TAB, self.MESSAGES_TAB):
663 if self._ledHandler is not None:
664 self._ledHandler.off()
666 @gtk_toolbox.log_exception(_moduleLogger)
667 def _on_close(self, *args, **kwds):
670 self._save_settings()
673 self._deviceState.close()
674 except AttributeError:
675 pass # Either None or close was removed (in Fremantle)
678 except AttributeError:
679 pass # Either None or close was removed (in Fremantle)
683 def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData):
685 For shutdown or save_unsaved_data, our only state is cookies and I think the cookie manager handles that for us.
686 For system_inactivity, we have no background tasks to pause
688 @note Hildon specific
692 for backendId in self.BACKENDS:
693 self._phoneBackends[backendId].clear_caches()
694 self._contactsViews[self._selectedBackendId].clear_caches()
697 if save_unsaved_data or shutdown:
698 self._save_settings()
700 self._errorDisplay.push_exception()
702 def _on_connection_change(self, connection, event, magicIdentifier):
704 @note Hildon specific
709 status = event.get_status()
710 error = event.get_error()
711 iap_id = event.get_iap_id()
712 bearer = event.get_bearer_type()
714 if status == conic.STATUS_CONNECTED:
716 self._spawn_attempt_login()
717 elif status == conic.STATUS_DISCONNECTED:
719 self._defaultBackendId = self._selectedBackendId
720 self._change_loggedin_status(self.NULL_BACKEND)
722 self._errorDisplay.push_exception()
724 def _on_window_state_change(self, widget, event, *args):
726 @note Hildon specific
729 if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
730 self._isFullScreen = True
732 self._isFullScreen = False
734 self._errorDisplay.push_exception()
736 def _on_key_press(self, widget, event, *args):
738 @note Hildon specific
740 RETURN_TYPES = (gtk.keysyms.Return, gtk.keysyms.ISO_Enter, gtk.keysyms.KP_Enter)
743 event.keyval == gtk.keysyms.F6 or
744 event.keyval in RETURN_TYPES and event.get_state() & gtk.gdk.CONTROL_MASK
746 if self._isFullScreen:
747 self._window.unfullscreen()
749 self._window.fullscreen()
750 elif event.keyval == gtk.keysyms.l and event.get_state() & gtk.gdk.CONTROL_MASK:
751 with open(constants._user_logpath_, "r") as f:
752 logLines = f.xreadlines()
753 log = "".join(logLines)
754 self._clipboard.set_text(str(log))
756 event.keyval in (gtk.keysyms.w, gtk.keysyms.q) and
757 event.get_state() & gtk.gdk.CONTROL_MASK
759 self._window.destroy()
760 elif event.keyval == gtk.keysyms.r and event.get_state() & gtk.gdk.CONTROL_MASK:
761 self._refresh_active_tab()
763 self._errorDisplay.push_exception()
765 def _on_clearcookies_clicked(self, *args):
767 self._phoneBackends[self._selectedBackendId].logout()
768 self._accountViews[self._selectedBackendId].clear()
769 self._historyViews[self._selectedBackendId].clear()
770 self._messagesViews[self._selectedBackendId].clear()
771 self._contactsViews[self._selectedBackendId].clear()
772 self._change_loggedin_status(self.NULL_BACKEND)
774 self._spawn_attempt_login(True)
776 self._errorDisplay.push_exception()
778 def _on_notebook_switch_page(self, notebook, page, pageIndex):
780 self._reset_tab_refresh()
782 didRecentUpdate = False
783 didMessagesUpdate = False
785 if pageIndex == self.RECENT_TAB:
786 didRecentUpdate = self._historyViews[self._selectedBackendId].update()
787 elif pageIndex == self.MESSAGES_TAB:
788 didMessagesUpdate = self._messagesViews[self._selectedBackendId].update()
789 elif pageIndex == self.CONTACTS_TAB:
790 self._contactsViews[self._selectedBackendId].update()
791 elif pageIndex == self.ACCOUNT_TAB:
792 self._accountViews[self._selectedBackendId].update()
794 if didRecentUpdate or didMessagesUpdate:
795 if self._ledHandler is not None:
796 self._ledHandler.off()
798 self._errorDisplay.push_exception()
800 def _set_tab_refresh(self, *args):
802 pageIndex = self._notebook.get_current_page()
803 child = self._notebook.get_nth_page(pageIndex)
804 self._notebook.get_tab_label(child).set_text("Refresh?")
806 self._errorDisplay.push_exception()
809 def _reset_tab_refresh(self, *args):
811 pageIndex = self._notebook.get_current_page()
812 child = self._notebook.get_nth_page(pageIndex)
813 self._notebook.get_tab_label(child).set_text(self._originalCurrentLabels[pageIndex])
815 self._errorDisplay.push_exception()
818 def _on_tab_refresh(self, *args):
820 self._refresh_active_tab()
821 self._reset_tab_refresh()
823 self._errorDisplay.push_exception()
826 def _on_sms_clicked(self, number, message):
828 assert number, "No number specified"
829 assert message, "Empty message"
831 loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
834 self._errorDisplay.push_exception()
838 self._errorDisplay.push_message(
839 "Backend link with GoogleVoice is not working, please try again"
845 self._phoneBackends[self._selectedBackendId].send_sms(number, message)
846 hildonize.show_information_banner(self._window, "Sending to %s" % number)
847 _moduleLogger.info("Sending SMS to %s" % number)
850 self._errorDisplay.push_exception()
853 self._dialpads[self._selectedBackendId].clear()
855 self._errorDisplay.push_exception()
857 def _on_dial_clicked(self, number):
859 assert number, "No number to call"
861 loggedIn = self._phoneBackends[self._selectedBackendId].is_authed()
864 self._errorDisplay.push_exception()
868 self._errorDisplay.push_message(
869 "Backend link with GoogleVoice is not working, please try again"
875 assert self._phoneBackends[self._selectedBackendId].get_callback_number() != "", "No callback number specified"
876 self._phoneBackends[self._selectedBackendId].call(number)
877 hildonize.show_information_banner(self._window, "Calling %s" % number)
878 _moduleLogger.info("Calling %s" % number)
881 self._errorDisplay.push_exception()
884 self._dialpads[self._selectedBackendId].clear()
886 self._errorDisplay.push_exception()
888 def _on_contact_import(self, *args):
890 csvFilter = gtk.FileFilter()
891 csvFilter.set_name("Contacts")
892 csvFilter.add_pattern("*.csv")
893 importFileChooser = gtk.FileChooserDialog(
897 importFileChooser.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
898 importFileChooser.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
900 importFileChooser.set_property("filter", csvFilter)
901 userResponse = importFileChooser.run()
902 importFileChooser.hide()
903 if userResponse == gtk.RESPONSE_OK:
904 filename = importFileChooser.get_filename()
905 shutils.copy2(filename, self._fsContactsPath)
907 self._errorDisplay.push_exception()
909 def _on_menu_refresh(self, *args):
911 self._refresh_active_tab()
913 self._errorDisplay.push_exception()
915 def _on_paste(self, *args):
917 contents = self._clipboard.wait_for_text()
918 if contents is not None:
919 self._dialpads[self._selectedBackendId].set_number(contents)
921 self._errorDisplay.push_exception()
923 def _on_about_activate(self, *args):
925 dlg = gtk.AboutDialog()
926 dlg.set_name(constants.__pretty_app_name__)
927 dlg.set_version("%s-%d" % (constants.__version__, constants.__build__))
928 dlg.set_copyright("Copyright 2008 - LGPL")
929 dlg.set_comments("Dialcentral is a touch screen enhanced interface to your GoogleVoice account. This application is not affiliated with Google in any way")
930 dlg.set_website("http://gc-dialer.garage.maemo.org/")
931 dlg.set_authors(["<z2n@merctech.com>", "Eric Warnke <ericew@gmail.com>", "Ed Page <eopage@byu.net>"])
935 self._errorDisplay.push_exception()
941 failureCount, testCount = doctest.testmod()
943 print "Tests Successful"
950 _lock_file = os.path.join(constants._data_path_, ".lock")
951 #with gtk_toolbox.flock(_lock_file, 0):
952 gtk.gdk.threads_init()
954 if hildonize.IS_HILDON_SUPPORTED:
955 gtk.set_application_name(constants.__pretty_app_name__)
956 handle = Dialcentral()
957 if not PROFILE_STARTUP:
961 class DummyOptions(object):
967 if __name__ == "__main__":
968 logging.basicConfig(level=logging.DEBUG)
970 if len(sys.argv) > 1:
976 if optparse is not None:
977 parser = optparse.OptionParser()
978 parser.add_option("-t", "--test", action="store_true", dest="test", help="Run tests")
979 (commandOptions, commandArgs) = parser.parse_args()
981 commandOptions = DummyOptions()
984 if commandOptions.test: