diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/agent.vala | 139 | ||||
| -rw-r--r-- | src/bluez.vala | 2 | ||||
| -rw-r--r-- | src/service.vala | 30 |
3 files changed, 134 insertions, 37 deletions
diff --git a/src/agent.vala b/src/agent.vala index beb8f8b..9e568a4 100644 --- a/src/agent.vala +++ b/src/agent.vala @@ -1,48 +1,103 @@ [DBus (name = "org.bluez.Agent1")] public class Agent: Object { + public GLib.Menu menu; + public GLib.SimpleActionGroup actions; + private GLib.SimpleAction pin_action; + public string menu_path; + public string actions_path; + private MainLoop loop; private Bluetooth bluetooth; + private Notify.Notification? notification; + private string passkey; public Agent (Bluetooth bluez) { + // Menu + menu = new GLib.Menu (); + GLib.MenuItem item = new GLib.MenuItem ("", "notifications.pin"); + item.set_attribute_value ("x-canonical-type", new Variant.string ("com.canonical.snapdecision.textfield")); + item.set_attribute_value ("x-echo-mode-password", new Variant.boolean (false)); + menu.append_item (item); + + // Actions + actions = new GLib.SimpleActionGroup (); + pin_action = new GLib.SimpleAction.stateful ("pin", null, new Variant.string ("")); + pin_action.change_state.connect ((value) => { + this.passkey = value.get_string (); + }); + actions.add_action (pin_action); + loop = new MainLoop (null, false); bluetooth = bluez; Notify.init ("ayatana-indicator-bluetooth"); } - private bool sendNotification (string device_name, string body) + /* TODO: Add a better way to differentiate between rejected and cancelled errors, maybe with an enum */ + private bool sendNotification (string device_name, string body, bool need_input, bool have_actions) { - bool accepted = false; + bool accepted = !have_actions; + + notification = new Notify.Notification (@"Pair with $device_name?", body, "bluetooth-active"); + notification.closed.connect (() => { + accepted = false; + notification = null; + + if (loop.is_running ()) { + loop.quit (); + } + }); + + bool is_lomiri = AyatanaCommon.utils_is_lomiri (); - Notify.Notification notification = new Notify.Notification (@"Pair with $device_name?", body, "bluetooth-active"); - bool bLomiri = AyatanaCommon.utils_is_lomiri (); + if (is_lomiri) { + if (have_actions) { + notification.set_hint ("x-lomiri-snap-decisions", true); + notification.set_hint ("x-lomiri-private-affirmative-tint", "true"); + } - if (bLomiri) - { - notification.set_hint ("x-lomiri-snap-decisions", true); - notification.set_hint ("x-lomiri-private-affirmative-tint", "true"); + if (need_input) { + VariantBuilder actions_builder = new VariantBuilder (new VariantType ("a{sv}")); + actions_builder.add ("{sv}", "notifications", new Variant.string (actions_path)); + + VariantBuilder builder = new VariantBuilder (new VariantType ("a{sv}")); + builder.add ("{sv}", "busName", new Variant.string ("org.ayatana.indicator.bluetooth")); + builder.add ("{sv}", "menuPath", new Variant.string (menu_path)); + builder.add ("{sv}", "actions", actions_builder.end ()); + + notification.set_hint ("x-lomiri-private-menu-model", builder.end ()); + } } - notification.add_action("yes_id", "Yes", (notif, action) => { - loop.quit (); - accepted = true; - }); - notification.add_action("no_id", "No", (notif, action) => { - loop.quit (); - accepted = false; - }); + if (have_actions) { + notification.add_action("yes_id", "Yes", (notif, action) => { + loop.quit (); + notification = null; + accepted = true; + }); + notification.add_action("no_id", "No", (notif, action) => { + loop.quit (); + notification = null; + accepted = false; + }); + } + + if (!have_actions && !need_input) { + // Display-only notification. Make sure we don't time out. + notification.set_hint ("urgency", 2); + } - try - { + try { notification.show (); } - catch (Error pError) - { - warning ("Panic: Failed showing notification: %s", pError.message); + catch (Error e) { + warning ("Panic: Failed showing notification: %s", e.message); } - loop.run (); + if (have_actions) { + loop.run (); + } return accepted; } @@ -54,42 +109,53 @@ public class Agent: Object public void RequestConfirmation (GLib.ObjectPath object, uint32 passkey) throws RejectedError, GLib.DBusError, GLib.IOError { string body = "Are you sure you want to pair with passkey %06u?".printf (passkey); - bool confirmed = sendNotification (bluetooth.get_device_name (object), body); + bool confirmed = sendNotification (bluetooth.get_device_name (object), body, false, true); - if (confirmed) { - return; - } else { + if (!confirmed) { throw new RejectedError.ERROR ("Rejected by user"); } } public void RequestAuthorization (GLib.ObjectPath object) throws RejectedError, GLib.DBusError, GLib.IOError { - bool authorized = sendNotification (bluetooth.get_device_name (object), "Are you sure you want to pair with this device?"); + bool authorized = sendNotification (bluetooth.get_device_name (object), "Are you sure you want to pair with this device?", false, true); - if (authorized) { - return; - } else { + if (!authorized) { throw new RejectedError.ERROR ("Rejected by user"); } } - public string RequestPinCode (GLib.ObjectPath object) throws GLib.DBusError, GLib.IOError + public string RequestPinCode (GLib.ObjectPath object) throws RejectedError, GLib.DBusError, GLib.IOError { - return "123456"; + bool accepted = sendNotification (bluetooth.get_device_name (object), "Enter PIN for this device", true, true); + + if (!accepted) { + throw new RejectedError.ERROR ("Rejected by user"); + } + + return passkey; } public void DisplayPinCode (GLib.ObjectPath object, string pincode) throws GLib.DBusError, GLib.IOError { + sendNotification (bluetooth.get_device_name (object), @"Enter the PIN code $pincode on the other device", false, false); } - public uint32 RequestPasskey (GLib.ObjectPath object) throws GLib.DBusError, GLib.IOError + public uint32 RequestPasskey (GLib.ObjectPath object) throws RejectedError, GLib.DBusError, GLib.IOError { - return 123456; + bool accepted = sendNotification (bluetooth.get_device_name (object), "Enter passkey for this device", true, true); + + if (!accepted) { + throw new RejectedError.ERROR ("Rejected by user"); + } + + return passkey.to_int (); } public void DisplayPasskey (GLib.ObjectPath object, uint32 passkey, uint16 entered) throws GLib.DBusError, GLib.IOError { + string body = "Enter the passkey %06u on the other device".printf (passkey); + sendNotification (bluetooth.get_device_name (object), body, false, false); } public void Cancel () throws GLib.DBusError, GLib.IOError @@ -97,6 +163,11 @@ public class Agent: Object if (loop.is_running ()) { loop.quit (); } + + if (notification != null) { + notification.close (); + notification = null; + } } public void Release () throws GLib.DBusError, GLib.IOError diff --git a/src/bluez.vala b/src/bluez.vala index 6d08004..8d481f2 100644 --- a/src/bluez.vala +++ b/src/bluez.vala @@ -468,7 +468,7 @@ public class Bluez: Bluetooth, Object { try { - agent_manager.register_agent (new GLib.ObjectPath(path), "DisplayYesNo"); + agent_manager.register_agent (new GLib.ObjectPath(path), AyatanaCommon.utils_is_lomiri() ? "KeyboardDisplay" : "DisplayYesNo"); } catch (GLib.Error pError) { diff --git a/src/service.vala b/src/service.vala index e651811..cff90d3 100644 --- a/src/service.vala +++ b/src/service.vala @@ -30,9 +30,15 @@ public class Service: Object private SimpleActionGroup actions; private HashTable<string,Profile> profiles; private Bluetooth bluetooth; + private Agent agent; private DBusConnection connection; private uint exported_action_id; + + private uint exported_agent_action_id; + private uint exported_agent_menu_id; + private const string OBJECT_PATH = "/org/ayatana/indicator/bluetooth"; + private const string AGENT_OBJECT_PATH = "/org/ayatana/indicator/bluetooth/agent"; private void unexport () { @@ -47,6 +53,18 @@ public class Service: Object connection.unexport_action_group (exported_action_id); exported_action_id = 0; } + + if (exported_agent_menu_id != 0) + { + connection.unexport_menu_model (exported_agent_menu_id); + exported_agent_menu_id = 0; + } + + if (exported_agent_action_id != 0) + { + connection.unexport_action_group (exported_agent_action_id); + exported_agent_action_id = 0; + } } } @@ -54,6 +72,9 @@ public class Service: Object { actions = new SimpleActionGroup (); bluetooth = bluetooth_service; + agent = new Agent (bluetooth); + agent.actions_path = AGENT_OBJECT_PATH; + agent.menu_path = AGENT_OBJECT_PATH; profiles = new HashTable<string,Profile> (str_hash, str_equal); profiles.insert ("phone", new Phone (bluetooth, actions)); @@ -84,7 +105,7 @@ public class Service: Object null); bluetooth.agent_manager_ready.connect (() => { - bluetooth.add_agent ("/agent"); + bluetooth.add_agent (AGENT_OBJECT_PATH); }); loop = new MainLoop (null, false); @@ -101,7 +122,7 @@ public class Service: Object { try { - connection.register_object ("/agent", new Agent (bluetooth)); + connection.register_object (AGENT_OBJECT_PATH, agent); } catch (GLib.IOError pError) { @@ -119,6 +140,11 @@ public class Service: Object debug (@"exporting action group '$(OBJECT_PATH)'"); exported_action_id = connection.export_action_group (OBJECT_PATH, actions); + + exported_agent_action_id = connection.export_action_group (AGENT_OBJECT_PATH, + agent.actions); + exported_agent_menu_id = connection.export_menu_model (AGENT_OBJECT_PATH, + agent.menu); } catch (Error e) { |
