aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/agent.vala139
-rw-r--r--src/bluez.vala2
-rw-r--r--src/service.vala30
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)
{