From 913aaa16f40a670ae694caf74738edacf2b2f1b7 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Tue, 10 Apr 2012 14:33:53 +0100 Subject: [PATCH 1/3] Add a simple Gtk3 dialler --- examples/client/python/Makefile.am | 1 + examples/client/python/dialler.py | 194 ++++++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100755 examples/client/python/dialler.py diff --git a/examples/client/python/Makefile.am b/examples/client/python/Makefile.am index 1d35979..01b9e69 100644 --- a/examples/client/python/Makefile.am +++ b/examples/client/python/Makefile.am @@ -1,5 +1,6 @@ EXTRA_DIST = \ contact-list.py \ + dialler.py \ ensure-channel.py \ inspect-cm.py \ text-handler.py \ diff --git a/examples/client/python/dialler.py b/examples/client/python/dialler.py new file mode 100755 index 0000000..cb72b64 --- /dev/null +++ b/examples/client/python/dialler.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python +# encoding: utf-8 +# +# A simple Telepathy dialler, suitable for use when a nicer UI you're +# developing doesn't work yet. +# +# Copyright © 2012 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +from gi.repository import GLib +from gi.repository import GObject +from gi.repository import Gio +from gi.repository import Gtk +from gi.repository import TelepathyGLib as Tp + +GObject.threads_init() + +# not available via g-i +USER_ACTION_TIME_NOT_USER_ACTION = 0 +ACCOUNT_MANAGER_FEATURE_CORE = GLib.quark_from_string( + 'tp-account-manager-feature-core') + +class Dialler(GObject.GObject): + __gsignals__ = { + 'finished': (GObject.SIGNAL_RUN_FIRST, None, ()), + } + + def __init__(self): + GObject.GObject.__init__(self) + + self.dbus = Tp.DBusDaemon.dup() + self.am = Tp.AccountManager.dup() + + self.accounts = Gtk.ListStore(str, object) + self.potential_handlers = set() + self.handlers = Gtk.ListStore(str, str) + + self.window = Gtk.Window() + self.window.connect('delete-event', Gtk.main_quit) + self.grid = Gtk.Grid() + self.grid.props.margin = 6 + self.grid.props.column_spacing = 6 + self.grid.props.row_spacing = 6 + self.window.add(self.grid) + + row = 0 + + self.grid.attach(Gtk.Label('local account:'), 0, row, 1, 1) + self.account_combo = Gtk.ComboBox.new_with_model(self.accounts) + renderer = Gtk.CellRendererText() + self.account_combo.pack_start(renderer, True) + self.account_combo.add_attribute(renderer, "text", 0) + self.grid.attach(self.account_combo, 1, row, 1, 1) + row += 1 + + self.grid.attach(Gtk.Label('remote target:'), 0, row, 1, 1) + self.target_entry = Gtk.Entry() + self.target_entry.set_text('smcv@example.com') + self.grid.attach(self.target_entry, 1, row, 1, 1) + row += 1 + + self.grid.attach(Gtk.Label('preferred Handler:'), 0, row, 1, 1) + self.handler_combo = Gtk.ComboBox.new_with_model(self.handlers) + renderer = Gtk.CellRendererText() + self.handler_combo.pack_start(renderer, True) + self.handler_combo.add_attribute(renderer, "text", 0) + self.grid.attach(self.handler_combo, 1, row, 1, 1) + row += 1 + + self.dial_button = Gtk.Button('dial') + self.dial_button.connect('clicked', self._dial_clicked) + self.grid.attach(self.dial_button, 0, row, 2, 1) + row += 1 + + self.dial_button.props.sensitive = False + self.window.show_all() + + self.am.prepare_async([], self._am_cb, None) + Gio.bus_get(Gio.BusType.SESSION, None, + self._session_bus_cb, None) + + def _am_cb(self, am, result, user_data): + am.prepare_finish(result) + + for account in am.get_valid_accounts(): + assert(account.object_path.startswith(Tp.ACCOUNT_OBJECT_PATH_BASE)) + + tail = account.object_path[len(Tp.ACCOUNT_OBJECT_PATH_BASE):] + self.accounts.append([account.get_display_name() or tail, account]) + + self.account_combo.set_active(0) + self.poll() + + def poll(self): + if (self.am.is_prepared(ACCOUNT_MANAGER_FEATURE_CORE) and + len(self.handlers) > 0 and + len(self.potential_handlers) == 0): + self.dial_button.props.sensitive = True + + def _session_bus_cb(self, session_bus, result, user_data): + self.session_bus = Gio.bus_get_finish(result) + + self.session_bus.call('org.freedesktop.DBus', + '/org/freedesktop/DBus', + 'org.freedesktop.DBus', + 'ListNames', + GLib.Variant("()", ()), + GLib.VariantType.new("(as)"), + Gio.DBusCallFlags.NONE, + -1, + None, + self._names_cb, + None) + + def _names_cb(self, session_bus, result, user_data): + names = set(session_bus.call_finish(result)[0]) + + self.handlers.append(['(no preference)', '']) + self.handler_combo.set_active(0) + + for name in sorted(names): + if name.startswith(Tp.CLIENT_BUS_NAME_BASE): + self.potential_handlers.add(name) + Gio.DBusProxy.new(session_bus, + Gio.DBusProxyFlags.NONE, + None, + name, + '/' + name.replace('.', '/'), + Tp.IFACE_CLIENT_HANDLER, + None, + self._potential_handler_cb, + name) + + self.poll() + + def _potential_handler_cb(self, proxy, result, name): + self.potential_handlers.remove(name) + proxy = Gio.DBusProxy.new_finish(result) + filters = proxy.get_cached_property("HandlerChannelFilter") + if filters is not None: + for asv in filters: + if asv.get(Tp.PROP_CHANNEL_CHANNEL_TYPE) == Tp.IFACE_CHANNEL_TYPE_CALL: + tail = name[len(Tp.CLIENT_BUS_NAME_BASE):] + self.handlers.append([tail, name]) + break + + self.poll() + + def _dial_clicked(self, button): + tree_iter = self.account_combo.get_active_iter() + account = self.accounts[tree_iter][1] + tree_iter = self.handler_combo.get_active_iter() + handler = self.handlers[tree_iter][1] + + acr = Tp.AccountChannelRequest.new(account, + { + Tp.PROP_CHANNEL_CHANNEL_TYPE: + Tp.IFACE_CHANNEL_TYPE_CALL, + Tp.PROP_CHANNEL_TARGET_HANDLE_TYPE: + int(Tp.HandleType.CONTACT), + Tp.PROP_CHANNEL_TARGET_ID: self.target_entry.get_text(), + Tp.PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO: True, + }, + USER_ACTION_TIME_NOT_USER_ACTION) + acr.create_and_observe_channel_async(handler, None, + self._create_call_cb, None) + + def _create_call_cb(self, acr, result, user_data): + channel = acr.create_and_observe_channel_finish(result) + print("accepting channel %s" % channel) + channel.accept_async(self._accept_cb, None) + + def _accept_cb(self, channel, result, user_data): + channel.accept_finish(result) + print("accepted channel %s" % channel) + +if __name__ == '__main__': + Tp.debug_set_flags("all") + + dialler = Dialler() + Gtk.main() -- 1.7.9.5